求助:SQL计算员工上月BusinessTime内的休假时长
统计员工工作时段内休假时长的解决方案
需求
统计员工上月处于**BusinessTime(工作时段)**内的休假时长。示例:员工周五8:00至下周一17:00休假,按周一至周五8:00-17:00的工作规则,休假时长计为18小时。
现有数据
TechTimeOff表
记录员工上月的休假实例,需计算DurationOff列(分钟或小数形式):
| TechID | TechName | TimeOff_From | TimeOff_To | TimeOff_ID | From(MinuteOfTheMonth) | To(MinuteOfTheMonth) | *DurationOff |
|---|---|---|---|---|---|---|---|
| 1 | Beaver | 2023-02-01 08:00 | 2023-02-01 16:30 | 23 | 480 | 990 | 8.50 hrs (或 510 mins) |
| 2 | Wally | 2023-02-01 08:00 | 2023-02-03 17:00 | 24 | 480 | 3060 | 27.00 hrs (或 1620 mins) |
| 3 | Ward | 2023-02-01 08:00 | 2023-02-06 17:00 | 25 | 480 | 6120 | 36.00 hrs (或 2160 mins) |
Calendar表
记录上月每一分钟的信息,非工作时段的BusinessTime标记为'No':
| Date&Time | DayName | MinuteOfTheMonth | HourOfTheDay | BusinessTime |
|---|---|---|---|---|
| 2023-02-01 07:59:00 | Wednesday | 479 | 7 | No |
| 2023-02-01 08:00:00 | Wednesday | 480 | 8 | Yes |
| 2023-02-01 16:30:00 | Wednesday | 990 | 16 | Yes |
| 2023-02-03 17:00:00 | Friday | 3060 | 17 | Yes |
| 2023-02-04 08:00:00 | Saturday | 1920 | 8 | No |
| 2023-02-06 17:00:00 | Monday | 6120 | 17 | Yes |
原思路问题
想将TechTimeOff表的休假时段拆分为每分钟一行,再关联Calendar表过滤非工作时段,但不知如何实现时段拆分。
最优解决方案
无需拆分休假时段,直接利用Calendar表的分钟数据关联统计,效率更高:
SQL实现代码
SELECT t.TechID, t.TechName, t.TimeOff_ID, t.TimeOff_From, t.TimeOff_To, COUNT(c.MinuteOfTheMonth) AS TotalMinutesOff, ROUND(COUNT(c.MinuteOfTheMonth) / 60.0, 2) AS DurationOff_Hrs FROM TechTimeOff t JOIN Calendar c ON c.MinuteOfTheMonth BETWEEN t.[From(MinuteOfTheMonth)] AND t.[To(MinuteOfTheMonth)] AND c.BusinessTime = 'Yes' GROUP BY t.TechID, t.TechName, t.TimeOff_ID, t.TimeOff_From, t.TimeOff_To ORDER BY t.TechID;
方案说明
- 通过
JOIN关联两张表,筛选出休假时段内属于工作时段的所有分钟记录 - 按员工及休假实例分组,统计符合条件的分钟总数
- 将分钟数转换为小时数(保留两位小数),得到最终的工作时段内休假时长
备选拆分时段方案(不推荐)
如果一定要拆分休假时段到每分钟,可使用递归CTE实现,但会生成大量行,性能较差:
WITH TimeOffMinutes AS ( SELECT TechID, TechName, TimeOff_ID, [From(MinuteOfTheMonth)] AS CurrentMinute, [To(MinuteOfTheMonth)] AS EndMinute FROM TechTimeOff UNION ALL SELECT TechID, TechName, TimeOff_ID, CurrentMinute + 1, EndMinute FROM TimeOffMinutes WHERE CurrentMinute < EndMinute ) SELECT tom.TechID, tom.TechName, tom.TimeOff_ID, COUNT(c.MinuteOfTheMonth) AS TotalMinutesOff, ROUND(COUNT(c.MinuteOfTheMonth) / 60.0, 2) AS DurationOff_Hrs FROM TimeOffMinutes tom JOIN Calendar c ON c.MinuteOfTheMonth = tom.CurrentMinute AND c.BusinessTime = 'Yes' GROUP BY tom.TechID, tom.TechName, tom.TimeOff_ID ORDER BY tom.TechID OPTION (MAXRECURSION 0); -- 需开启无限递归,避免时段过长报错
内容的提问来源于stack exchange,提问作者Laura Bullard
相关产品推荐
相关产品推荐

