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

在SQL Server 2012中按7天窗口分组轮班数据,sql,sql-server,datetime,sql-server-2012,recursive-query,Sql,Sql Server,Datetime,Sql Server 2012,Recursive Query,我想做的是计算每个员工在任何给定的7天期间的轮班数和工作小时数。为了实现这一点,我需要识别和分组轮班的“孤岛”。请注意,这7天期限与日历周无关,这7天期限的开始和结束时间因员工而异。这与她过去提出的其他类似问题不同 我有一张这样的桌子: Person ID Start Date End Date Start time End time Hours Worked 12345 06-07-20 06-07-20 6:00 AM 7:

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

我有一张这样的桌子:

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
我希望分组并总结上述数据,如下所示:

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天窗口

第二天的7天窗口是从
22-07-20
28-07-20
,这意味着7天窗口的开始日期必须是动态的,并且基于数据,即非常数,这使得这是一项复杂的任务

还请注意,一名员工可能在一天内工作多个班次,并且这些班次可能不是连续的


我在玩
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
    datatype,时间存储为
    time


    • 我想你需要一个递归的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
        datatype,时间存储为
        time



      7月6日是星期一。那有关系吗?除了第一个日期(或星期一的日期)之外,是否还有其他标准可以开始7天的时间段?似乎
      开始/结束时间
      是varchar值。。。。我说的对吗?而且
      开始/结束日期
      似乎是varchar数据类型。。。这导致需要进行数据转换,这可能是性能缓慢的原因。因此,在一个周期和下一个周期之间仅存在1天或更长的“间隙”就可以标记差异?或者我也必须将可能连续14天的序列分成7天的块?您的数据有两个“岛”:7月6日至7月11日,以及7月22日至7月28日。如果是7月22日至7月31日,或8月4日/5日,情况会怎样?在这种情况下,随后的每个7天组取决于数据的差距或前一个7天组的形状。如果没有递归,这是不可能做到的,所以请遵循@GMB的建议……7月6日是星期一。那有关系吗?除了第一个日期(或星期一的日期)之外,是否还有其他标准可以开始7天的时间段?似乎
      开始/结束时间
      是varchar值。。。。我说的对吗?而且
      开始/结束日期
      似乎是varchar数据类型。。。这导致需要进行数据转换,这可能是性能缓慢的原因。因此,在一个周期和下一个周期之间仅存在1天或更长的“间隙”就可以标记差异?或者我也必须将可能连续14天的序列分成7天的块?您的数据有两个“岛”:7月6日至7月11日,以及7月22日至7月28日。如果是7月22日至7月31日,或8月4日/5日,情况会怎样?在这种情况下,随后的每个7天组取决于数据的差距或前一个7天组的形状。如果没有递归,这是做不到的,所以请遵循@GMB的建议…谢谢你的解决方案,但我只是有一个后续,因为我不熟悉递归CTE。第14行出现
      无效列名“rn”
      错误。知道为什么吗?非常感谢。@ashvin10:我的错,这个专栏也必须转到
      select
      子句。已修复。在完整数据集上运行时遇到此错误:
      语句终止。在语句完成之前,最大递归100已用尽。
      关于如何克服某些限制的任何想法,@GMB?@ashvin10:您可以在查询的最后添加
      选项(maxrecursion 0)
      。事实上,我自己就知道了,@GMB。对于其他好奇的人,我遵循@DanielSander的建议,创建了一个带有行号列的临时表,然后在其上运行递归CTE,比如说
      SELECT t.*,ROW_NUMBER()OVER(按[Person ID]划分,按[Start Date]排序),作为rn进入考勤日志,并从考勤日志中删除谢谢你的解决方案,但我只是有一个跟进,因为我不熟悉递归CTE。第14行出现
      无效列名“rn”
      错误。知道为什么吗?非常感谢。@ashvin10:我的错,这个专栏也必须转到
      select
      子句。已修复。在完整数据集上运行时遇到此错误:
      语句终止。在语句完成之前,最大递归100已用尽。
      关于如何克服某些限制的任何想法,@GMB?@ashvin10:您可以在查询的最后添加
      选项(maxrecursion 0)
      。事实上,我自己就知道了,@GMB。对于其他好奇的人,我遵循@DanielSander的建议,创建了一个带有ROW_NUMBER列的临时表,然后在其上运行递归CTE,如so
      SELECT t.*,ROW_NU