在 SQL Server 2012 中按 7 天 windows 分组轮班数据

Grouping shift data by 7-day windows in SQL Server 2012

我想做的是计算每个员工在任何给定的 7 天内的班次和工作小时数。为了实现这一点,我需要识别和分组 'islands' 班次。请注意,这 7 天期限与日历周无关,并且这 7 天期限的开始和结束时间因员工而异。这与她过去问过的其他类似问题不同。

我有一个 table 这样的:

Person ID   Start Date  End Date    Start time      End time    Hours Worked
12345       06-07-20    06-07-20    6:00 AM         7:45 AM     1.75
12345       06-07-20    06-07-20    8:15 AM         8:45 AM     0.50
12345       06-07-20    06-07-20    9:19 AM         9:43 AM     0.40
12345       08-07-20    08-07-20    12:00 AM        12:39 AM    0.65
12345       09-07-20    09-07-20    10:05 PM        11:59 PM    1.90
12345       11-07-20    11-07-20    4:39 PM         4:54 PM     0.25
12345       22-07-20    22-07-20    7:00 AM         7:30 AM     0.50
12345       23-07-20    23-07-20    1:00 PM         3:00 PM     2.00
12345       24-07-20    24-07-20    9:14 AM         9:35 AM     0.35
12345       27-07-20    27-07-20    4:00 PM         6:00 PM     2.00
12345       27-07-20    27-07-20    2:00 PM         4:00 PM     2.00
12345       28-07-20    28-07-20    9:00 AM         10:00 AM    1.00
12345       28-07-20    28-07-20    4:39 AM         4:59 AM     0.34

我想对上面的数据进行分组总结:

Person ID   From        To          Number of shifts    Number of Hours
12345       06-07-20    11-07-20    6                   5.45
12345       22-07-20    28-07-20    7                   8.19

请注意,员工 12345 的第一个分组从 06-07-20 开始并在 11-07-20 结束,因为这些轮班属于 06-07-20 - 13-07-20 7 -天 window.

下一天 7 天 window 是从 22-07-2028-07-20,这意味着 7 天 window 的开始日期必须是动态的并基于数据,即不是常量,这使得这是一项复杂的任务。

另请注意,一名员工可能一天工作多个班次,而且这些班次可能不是连续的。

我正在尝试将 DATEDIFF()LAG()LEAD() 一起使用,但无法到达我想要的位置。任何帮助将不胜感激。

我认为您需要一个递归 CTE 来解决这个问题。这个想法是枚举每个人的班次,然后迭代遍历数据集,同时跟踪周期的第一个日期——当一个周期的开始和当前日期之间有超过 7 天的时间,开始日期重置,新组开始。

with recursive 
    data as (select t.*, row_number() over(partition by personid order by start_date) rn from mytable t)
    cte as (
        select personid, start_date, start_date end_date, hours_worked, rn 
        from data 
        where rn = 1
        union all
        select 
            c.personid, 
            case when d.start_date > dateadd(day, 7, c.start_date) then d.start_date else c.start_date end,
            d.start_date,
            d.hours_worked,
            d.rn
        from cte c
        inner join data d on d.personid = c.personid and d.rn = c.rn + 1
    )
select personid, start_date, max(start_date) end_date, count(*) no_shifts, sum(hours_worked)
from cte
group by personid, start_date

这假设:

  • 日期不会跨越多天,如示例数据所示

  • 日期存储为 date 数据类型,时间存储为 time