查询时间序列,使用与新日重叠的班次的开始日期更新每一行

时间:2014-12-19 03:53:31

标签: sql-server tsql sql-server-2008-r2

我有一张表,其中包含员工的一些班次信息

+-------+------------+------------------+------------+------------------+------------+
| empid | StartDate  |    StartTime     |  EndDate   |     EndTime      | ShiftDate  |
+-------+------------+------------------+------------+------------------+------------+
|   391 | 2014-12-16 | 20:00:00.0000000 | 2014-12-16 | 22:00:00.0000000 | ?          |
|   391 | 2014-12-16 | 22:00:00.0000000 | 2014-12-16 | 22:15:00.0000000 | ?          |
|   391 | 2014-12-16 | 22:15:00.0000000 | 2014-12-17 | 00:00:00.0000000 | ?          |
|   391 | 2014-12-17 | 00:00:00.0000000 | 2014-12-17 | 00:45:00.0000000 | ?          |
|   391 | 2014-12-17 | 00:45:00.0000000 | 2014-12-17 | 02:30:00.0000000 | ?          |
|   391 | 2014-12-17 | 02:30:00.0000000 | 2014-12-17 | 02:45:00.0000000 | ?          |
|   391 | 2014-12-17 | 02:45:00.0000000 | 2014-12-17 | 04:30:00.0000000 | ?          |
+-------+------------+------------------+------------+------------------+------------+
|   391 | 2014-12-17 | 20:00:00.0000000 | 2014-12-17 | 21:45:00.0000000 | ?          |
|   391 | 2014-12-17 | 21:45:00.0000000 | 2014-12-17 | 22:00:00.0000000 | ?          |
|   391 | 2014-12-17 | 22:00:00.0000000 | 2014-12-18 | 00:00:00.0000000 | ?          |
|   391 | 2014-12-18 | 00:00:00.0000000 | 2014-12-18 | 00:45:00.0000000 | ?          |
|   391 | 2014-12-18 | 00:45:00.0000000 | 2014-12-18 | 02:30:00.0000000 | ?          |
|   391 | 2014-12-18 | 02:30:00.0000000 | 2014-12-18 | 02:45:00.0000000 | ?          |
|   391 | 2014-12-18 | 02:45:00.0000000 | 2014-12-18 | 04:30:00.0000000 | ?          |
+-------+------------+------------------+------------+------------------+------------+

我需要使用StartDate为此班次更新ShiftDate列。所以下面是2个不同的班次,每个班次超过2天。每行的ShiftDate第一次转换应该是2014-12-16,第二次转换,每一行的ShiftDate应该是2014-12-17。

预期结果:

+-------+------------+------------------+------------+------------------+------------+
| empid | StartDate  |    StartTime     |  EndDate   |     EndTime      | ShiftDate  |
+-------+------------+------------------+------------+------------------+------------+
|   391 | 2014-12-16 | 20:00:00.0000000 | 2014-12-16 | 22:00:00.0000000 | 2014-12-16 |
|   391 | 2014-12-16 | 22:00:00.0000000 | 2014-12-16 | 22:15:00.0000000 | 2014-12-16 |
|   391 | 2014-12-16 | 22:15:00.0000000 | 2014-12-17 | 00:00:00.0000000 | 2014-12-16 |
|   391 | 2014-12-17 | 00:00:00.0000000 | 2014-12-17 | 00:45:00.0000000 | 2014-12-16 |
|   391 | 2014-12-17 | 00:45:00.0000000 | 2014-12-17 | 02:30:00.0000000 | 2014-12-16 |
|   391 | 2014-12-17 | 02:30:00.0000000 | 2014-12-17 | 02:45:00.0000000 | 2014-12-16 |
|   391 | 2014-12-17 | 02:45:00.0000000 | 2014-12-17 | 04:30:00.0000000 | 2014-12-16 |
+-------+------------+------------------+------------+------------------+------------+
|   391 | 2014-12-17 | 20:00:00.0000000 | 2014-12-17 | 21:45:00.0000000 | 2014-12-17 |
|   391 | 2014-12-17 | 21:45:00.0000000 | 2014-12-17 | 22:00:00.0000000 | 2014-12-17 |
|   391 | 2014-12-17 | 22:00:00.0000000 | 2014-12-18 | 00:00:00.0000000 | 2014-12-17 |
|   391 | 2014-12-18 | 00:00:00.0000000 | 2014-12-18 | 00:45:00.0000000 | 2014-12-17 |
|   391 | 2014-12-18 | 00:45:00.0000000 | 2014-12-18 | 02:30:00.0000000 | 2014-12-17 |
|   391 | 2014-12-18 | 02:30:00.0000000 | 2014-12-18 | 02:45:00.0000000 | 2014-12-17 |
|   391 | 2014-12-18 | 02:45:00.0000000 | 2014-12-18 | 04:30:00.0000000 | 2014-12-17 |

+ ------- + ------------ + ------------------ + ------ ------ + ------------------ + ------------ +

我无法确定从给定行确定转换日期的方法。当它是新的班次时,Shift StartTime将永远不会与Shift的前一个EndTime相同。

我正在使用SQL 2008 R2

任何让我朝着正确方向前进的建议都会很棒!

1 个答案:

答案 0 :(得分:1)

使用递归CTE:

;WITH
    CTE1 AS
    (
        SELECT  empid, StartDate, StartTime, EndDate, EndTime,
                ROW_NUMBER() OVER (PARTITION BY empid ORDER BY StartDate, StartTime) AS RowNumber
        FROM    EmployeeShift
    ),
    CTE2 AS (
        SELECT  empid, StartDate, StartTime, EndDate, EndTime, 
                StartDate AS ShiftDate,
                RowNumber
        FROM    CTE1
        WHERE   RowNumber = 1

        UNION ALL

        SELECT  c1.empid, c1.StartDate, c1.StartTime, c1.EndDate, c1.EndTime,
                CASE
                    WHEN c1.StartDate = c2.EndDate AND c1.StartTime = c2.EndTime THEN c2.ShiftDate
                    ELSE c1.StartDate
                END AS ShiftDate,
                c1.RowNumber
        FROM    CTE1    c1
        INNER JOIN CTE2 c2 ON c1.empid = c2.empid AND c1.RowNumber = c2.RowNumber + 1
    )

SELECT * FROM CTE2
OPTION (MAXRECURSION 0)

评论员要求它的内部结构,所以在这里:

  1. 每个班次编号由每位员工编号为1,2,3,...,n,并根据其开始和结束日期进行排序。这是CTE1
  2. 对于第一个班次条目,ShiftDate与其StartDate相同。这是CTE2的上半部分。
  3. 从第一个班次条目中,它递归地通过该员工的下一个条目。如果下一个条目的开始时间与之前条目的结束时间相同,则它继续前一个ShiftDate。否则,它会将ShiftDate设置为当前班次StartDate