2013-06-26 78 views
1

我有一個表具有以下有序數據塊:結合連續SQL行,每隔一個,一個警告

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 

我需要的是得到一個類型的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

有一些使用Windows函數的解決方案,如ROW_NUMBER。請稍微解釋一下行的順序。是「日期」專欄來訂購它們還是有更精確的東西?如果當天有兩種相同的類型呢? –

+0

請同時指出您使用的是哪個版本的SQL Server。 –

回答

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使其更易於閱讀。

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

我想表達這種最簡單的方法是用相關子查詢:

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; 

這是使以下假設:

  1. 下一個「B」是在相同的日期
  2. 但是您代表的是次,您可以拿取差異獲得持續時間
  3. 該記錄按(date,timeX)排序。