【问题标题】:Grouping shift data by 7-day windows in SQL Server 2012在 SQL Server 2012 中按 7 天窗口对班次数据进行分组
【发布时间】:2020-10-09 09:38:33
【问题描述】:

我想做的是计算每个员工在任何给定的 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

请注意,员工 12345 的第一个分组从 06-07-20 开始,到 11-07-20 结束,因为这些班次在 06-07-20 - 13-07-20 7 天窗口内。

接下来的 7 天窗口是从 22-07-2028-07-20,这意味着 7 天窗口的开始日期必须是动态的并且基于数据,即不是恒定的,这使得这是一项复杂的任务.

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

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

【问题讨论】:

  • 7 月 6 日是星期一。这有关系吗?除了第一个日期(或星期一的日期)之外,还有其他标准可以开始为期 7 天的时间段吗?
  • 似乎 Start / End time 是 varchar 值....我说的对吗?
  • 另外Start/End Date似乎是varchar数据类型...这导致需要数据转换,这可能是性能缓慢的原因。
  • 所以一个时期和下一个时期之间只有 1 天或更多的“差距”标志着差异?还是我还必须将可能连续 14 天的系列分成 7 天的部分?您的数据有两个“孤岛”:7 月 6 日至 7 月 11 日和 7 月 22 日至 7 月 28 日。如果是 7 月 22 日至 7 月 31 日,或 8 月 4 日/5 日,那该怎么办?
  • 在这种情况下,每个后续的 7 天组都取决于数据中的差距或前 7 天组的形状。没有递归就无法做到这一点,因此请遵循@GMB 的建议...

标签: sql sql-server datetime sql-server-2012 recursive-query


【解决方案1】:

我认为你需要一个递归 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

【讨论】:

  • 感谢您的解决方案,但我只需要跟进,因为我不熟悉递归 CTE。我在第 14 行收到 Invalid column name 'rn' 错误。知道为什么吗?非常感谢。
  • @ashvin10:我的错,这个专栏也必须使用select 子句。固定。
  • 在完整数据集上运行时遇到此错误:The statement terminated. The maximum recursion 100 has been exhausted before statement completion.@GMB 关于如何克服此限制的任何想法?
  • @ashvin10:您可以在查询的最后添加option(maxrecursion 0)
  • 实际上,我自己也想出来了,@GMB。对于其他好奇的人,我遵循 @DanielSander 的建议 here 并创建了一个带有 ROW_NUMBER 列的临时表,然后在其上运行递归 CTE,就像这样 SELECT t.* ,ROW_NUMBER() OVER(PARTITION BY [Person ID] ORDER BY [Start Date]) AS rn INTO Attendance_Logs_with_RN FROM Attendance Logs t . . .
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-21
  • 1970-01-01
  • 1970-01-01
  • 2016-02-17
  • 1970-01-01
  • 1970-01-01
  • 2016-08-11
相关资源
最近更新 更多