在SQL Server 2012中按7天窗口分组轮班数据
我想做的是计算每个员工在任何给定的7天期间的轮班数和工作小时数。为了实现这一点,我需要识别和分组轮班的“孤岛”。请注意,这7天期限与日历周无关,这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:
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
这假定:
- 日期不会跨越多天,如示例数据所示
- 日期存储为
datatype,时间存储为date
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
这假定:
- 日期不会跨越多天,如示例数据所示
- 日期存储为
datatype,时间存储为date
time
开始/结束时间
是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,如soSELECT t.*,ROW_NU