我有一张包含以下有序数据的表格:
Employee TimeX TimeY Type Date
-----------------------------------------------
1 0800 0900 'A' 1/1/2013
1 0930 1300 'B' 1/1/2013
1 0600 0845 'A' 1/2/2013
1 0925 1300 'B' 1/2/2013
1 1100 1400 'A' 1/3/2013
1 0500 0700 'A' 1/4/2013
1 0715 0800 'B' 1/4/2013
对于每个匹配的对,我需要的是在类型A的TimeY和类型B的TimeX之间获得分钟数。由于我无法控制的设计,我无法将“A”和“B”连接在一起,而不仅仅是按时间戳顺序连接。遗憾的是,不能,我不能保证所有类型'A'的行都会跟着'B'类型的行,所以任何'A'都不会被'B'所忽略。但是,没有'B'会跟随另一个'B'。基本上,这是我想看到的:
Employee Duration
---------------------
1 30
1 40
1 15
有没有办法轻松完成这项工作?我在这里找到的最接近的解决方案涉及加入日期,但在这种情况下这不会起作用。我下午晚些时候提出的唯一可能的解决办法过于复杂而且没有过时。
编辑:感谢您的回复!这是一些令人印象深刻的SQL争吵!我选择了Marc的答案,因为它是最容易阅读的,但感谢Gordon为Marc的回答提供灵感,感谢Nenad在我尝试的过程中所做的努力。
答案 0 :(得分:1)
现在是凌晨2点,这可能是我写过的最丑陋的查询之一,我很确定有一些方法可以简化一些部分。但是,重要的是 - 它正在发挥作用:)
;WITH CTE1 AS
(
--first CTE is simply to get row numbering over all dates
SELECT *, ROW_NUMBER() OVER (ORDER BY [Date],[Type]) RN
FROM Table1
)
, RCTE1 AS
(
--recursive cte is going row-by-row checking if next type is same or different
SELECT *, 1 AS L FROM CTE1 WHERE RN =1
UNION ALL
--assigning same L if next is same, L+1 if different
SELECT c.*, CASE WHEN r.Type = c.Type THEN L ELSE L+1 END AS L
FROM RCTE1 r
INNER JOIN CTE1 c ON r.RN +1 = c.RN
)
, CTE2 AS
(
--here we search for same L values
SELECT *, ROW_NUMBER() OVER (PARTITION BY L ORDER BY RN DESC) RN2 FROM RCTE1
)
, CTE3 AS
(
--and eliminate the rows not needed (ie A in front of A)
SELECT *, ROW_NUMBER() OVER (PARTITION BY [Type] ORDER BY L) RN3
FROM CTE2 WHERE RN2 =1
)
-- at the end join CTE3 based on same RN3 and different type
SELECT *
-- and some datetime operations to get times from strings
, DATEDIFF(MI,DATEADD(MI,CAST(RIGHT(A.TimeY,2) AS INT) , DATEADD(HH,CAST(LEFT(A.TimeY,2) AS INT),0)), DATEADD(MI,CAST(RIGHT(B.TimeX,2) AS INT) , DATEADD(HH,CAST(LEFT(B.TimeX,2) AS INT),0))) AS Goal
FROM CTE3 a
INNER JOIN CTE3 B ON a.RN3 = b.RN3 AND a.[Type] = 'A' AND b.[Type] = 'B'
-- maxrecursion off so Recursive CTE can work
OPTION (MAXRECURSION 0)
<强> SQLFiddle DEMO 强>
答案 1 :(得分:1)
我认为最简单的表达方式是使用相关的子查询:
select t.employee, t.nextBtime - t.time
from (select t.*,
(select top 1 (case when type = 'B' then timeY end)
from t t2
where t2.employee = t.employee and
t2.date = t.date and
t2.timeX > t.timeX
order by t2.timeX
) nextBtime
from t
where type = 'A'
) t
where nextBtime is null;
这是做出以下假设:
date
,timeX
)排序。答案 2 :(得分:1)
SELECT
a.Employee,
a.TimeY,
b.TimeX
FROM Table1 a
CROSS APPLY
(
SELECT TOP(1) t.TimeX
FROM Table1 t
WHERE a.[Date] = t.[Date]
AND a.Employee = t.Employee
AND a.TimeY < t.TimeX
AND t.[Type] = 'B'
ORDER BY t.TimeX ASC
) b
WHERE a.[Type] = 'A'
ORDER BY a.Employee ASC
;
这实际上并没有进行减法,因为我不清楚TimeX和TimeY的类型。
这类似于相关的子查询答案,但我认为CROSS APPLY使其更容易阅读。