如何在SQL中计算所有员工的工作时间重叠时段
找出所有员工同时在岗的时段
现有一份员工工作时段列表,每位员工的工作时段各不相同,需要筛选出所有员工同时在岗的时段,剔除仅单个或部分员工在岗的时段。
测试数据
| sDate(日期) | startTime(开始时间) | endTime(结束时间) | name(姓名) |
|---|---|---|---|
| 2023-02-23 00:00:00.000 | 2023-02-23 08:00:00.000 | 2023-02-23 10:00:00.000 | John |
| 2023-02-23 00:00:00.000 | 2023-02-23 10:30:00.000 | 2023-02-23 12:00:00.000 | John |
| 2023-02-23 00:00:00.000 | 2023-02-23 13:00:00.000 | 2023-02-23 17:00:00.000 | John |
| 2023-02-23 00:00:00.000 | 2023-02-23 08:30:00.000 | 2023-02-23 09:00:00.000 | Anita |
| 2023-02-23 00:00:00.000 | 2023-02-23 09:30:00.000 | 2023-02-23 20:00:00.000 | Anita |
尝试的SQL代码(未得到预期结果)
DECLARE @tmpSchedules TABLE ( sDate DATETIME ,startTime DATETIME ,endTime DATETIME ,name varchar(255) ) INSERT INTO @tmpSchedules SELECT '20230223', '20230223 08:00', '20230223 10:00', 'John' UNION ALL SELECT '20230223', '20230223 10:30', '20230223 12:00', 'John' UNION ALL SELECT '20230223', '20230223 13:00', '20230223 17:00', 'John' UNION ALL SELECT '20230223', '20230223 08:30', '20230223 09:00', 'Anita' UNION ALL SELECT '20230223', '20230223 09:30', '20230223 20:00', 'Anita' select * from @tmpSchedules SELECT CA.sDate, CA.startTime, CA.endTime ,CASE WHEN LAG(CA.endTime) OVER (ORDER BY CA.starttime) > CA.startTime THEN 0 ELSE 1 END OVL FROM @tmpSchedules CA
预期输出
| sDate(日期) | startTime(开始时间) | endTime(结束时间) | Duration(时长/分钟) |
|---|---|---|---|
| 2023-02-23 00:00:00 | 2023-02-23 08:30:00 | 2023-02-23 09:00:00 | 30 |
| 2023-02-23 00:00:00 | 2023-02-23 09:30:00 | 2023-02-23 10:00:00 | 30 |
| 2023-02-23 00:00:00 | 2023-02-23 10:30:00 | 2023-02-23 12:00:00 | 90 |
| 2023-02-23 00:00:00 | 2023-02-23 13:00:00 | 2023-02-23 17:00:00 | 240 |
工作时段重叠可视化

解决方案
原代码使用LAG函数时,仅对所有记录按开始时间排序后判断前后时段是否重叠,未区分员工,也无法统计时段内的在岗员工数量,因此无法筛选出全员在岗的区间。
正确思路:
- 提取所有时间节点(员工的开始、结束时间),生成连续无间隙的时间区间;
- 统计每个区间内的在岗员工数量;
- 筛选出员工数量等于总员工数的区间,计算时长。
实现代码:
DECLARE @tmpSchedules TABLE ( sDate DATETIME ,startTime DATETIME ,endTime DATETIME ,name varchar(255) ) INSERT INTO @tmpSchedules SELECT '20230223', '20230223 08:00', '20230223 10:00', 'John' UNION ALL SELECT '20230223', '20230223 10:30', '20230223 12:00', 'John' UNION ALL SELECT '20230223', '20230223 13:00', '20230223 17:00', 'John' UNION ALL SELECT '20230223', '20230223 08:30', '20230223 09:00', 'Anita' UNION ALL SELECT '20230223', '20230223 09:30', '20230223 20:00', 'Anita' -- 获取总员工数 DECLARE @totalEmployees INT = (SELECT COUNT(DISTINCT name) FROM @tmpSchedules) -- 生成所有时间节点并排序 ;WITH TimePoints AS ( SELECT startTime AS pointTime FROM @tmpSchedules UNION SELECT endTime AS pointTime FROM @tmpSchedules ), -- 生成连续的时间区间 TimeIntervals AS ( SELECT tp1.pointTime AS intervalStart, tp2.pointTime AS intervalEnd FROM TimePoints tp1 JOIN TimePoints tp2 ON tp1.pointTime < tp2.pointTime -- 确保区间无间隙 WHERE NOT EXISTS ( SELECT 1 FROM TimePoints tp3 WHERE tp3.pointTime > tp1.pointTime AND tp3.pointTime < tp2.pointTime ) ) -- 筛选全员在岗的区间并计算时长 SELECT ts.sDate, ti.intervalStart AS startTime, ti.intervalEnd AS endTime, DATEDIFF(MINUTE, ti.intervalStart, ti.intervalEnd) AS Duration FROM TimeIntervals ti CROSS JOIN (SELECT DISTINCT sDate FROM @tmpSchedules) ts JOIN @tmpSchedules ts2 ON ti.intervalStart < ts2.endTime AND ti.intervalEnd > ts2.startTime GROUP BY ts.sDate, ti.intervalStart, ti.intervalEnd HAVING COUNT(DISTINCT ts2.name) = @totalEmployees ORDER BY ti.intervalStart
代码说明
TimePointsCTE:收集所有员工的开始、结束时间,去重后得到关键时间节点;TimeIntervalsCTE:将时间节点配对,生成连续无间隙的时间区间;- 最后关联员工时段表,统计每个区间的在岗员工数,筛选出全员在岗的区间并计算时长。
内容的提问来源于stack exchange,提问作者J Hoopes
相关产品推荐
相关产品推荐

