我有2张桌子
第一张桌子有
startprocesstime
和 endprocesstime
EmployeeID
、StartShift
和 EndShift
。
我需要检查
startprocesstime
和 Endprocesstime
是否仍在我的 shiftschedule
范围内。
示例:场景 1:开始进程时间
2023-11-18 4:30:000
结束进程时间 2023-11-18 4:31:000
2023-11-19 00:30:000
结束处理时间 2023-11-19 00:31:000
EmployeedID 4pm(StartShift) to 1am(EndShift).
一种方法是将流程与班次交叉连接,然后在每个流程涵盖的所有日期之间复制班次。请注意,如果该班次跨越午夜,则第一个候选班次可能会在流程开始的前一天开始。
然后可以使用标准测试
start1 < end2 AND start2 < end1
检查流程和班次日期/时间范围是否重叠。对于每次重叠,重叠量(以分钟为单位)可以计算为 DATEDIFF(minute, GREATEST(start1, start2), LEAST(end1, end2))
。
最后,由于多天内相同流程/班次组合可能会产生多个结果,因此可以使用
GROUP BY
将它们组合起来,并应用 SUM()
来总计计算出的重叠。
生成的查询类似于:
SELECT
P.Name AS PName
,CONVERT(SMALLDATETIME, P.Start) AS PStart
,CONVERT(SMALLDATETIME, P.Finish) AS PFinish
,SUM(DATEDIFF(minute, GREATEST(P.Start, SS.Start), LEAST(P.Finish, SS.Finish))) AS Minutes
,S.Name AS SName
,CONVERT(CHAR(5), S.Start, 8) AS SStart
,CONVERT(CHAR(5), S.Finish, 8) AS SFinish
FROM (
-- Process data plus some calculated fields
SELECT
P.*,
CONVERT(DATE, P.Start) AS StartDate,
DATEDIFF(day, P.Start, P.Finish) AS Days
FROM Process P
) P
CROSS JOIN (
-- Shift data plus a check for midnight crossover
SELECT
S.*,
CASE WHEN S.Start > S.Finish THEN 1 ELSE 0 END AS CrossOver
FROM Shift S
) S
CROSS APPLY (
-- Replicate shifts across all dates covered by each process
SELECT
CONVERT(DATETIME, DATEADD(day, G.value, P.StartDate)) + CONVERT(DATETIME, S.Start) AS Start,
CONVERT(DATETIME, DATEADD(day, G.value + S.Crossover, P.StartDate)) + CONVERT(DATETIME, S.Finish) AS Finish
FROM GENERATE_SERIES(-S.Crossover, P.Days) G
) SS
WHERE (P.Start < SS.Finish AND SS.Start < P.Finish) -- overlap test
GROUP BY
P.Name, P.Start, P.Finish
,S.Name, S.Start, S.Finish
ORDER BY P.Name, S.Name
带有一些附加测试数据的示例结果:
P姓名 | 开始 | P完成 | 分钟 | S姓名 | 开始 | S完成 |
---|---|---|---|---|---|---|
流程1 | 2023-11-18 04:30 | 2023-11-18 04:31 | 1 | 班次2 | 23:00 | 07:00 |
流程2 | 2023-11-19 00:30 | 2023-11-19 00:31 | 1 | 班次1 | 16:00 | 01:00 |
流程2 | 2023-11-19 00:30 | 2023-11-19 00:31 | 1 | 班次2 | 23:00 | 07:00 |
流程3 | 2023-11-22 23:45 | 2023-11-23 00:15 | 30 | 班次1 | 16:00 | 01:00 |
流程3 | 2023-11-22 23:45 | 2023-11-23 00:15 | 30 | 班次2 | 23:00 | 07:00 |
流程4 | 2023-11-24 23:45 | 2023-11-27 00:15 | 1110 | 班次1 | 16:00 | 01:00 |
流程4 | 2023-11-24 23:45 | 2023-11-27 00:15 | 990 | 班次2 | 23:00 | 07:00 |
流程4 | 2023-11-24 23:45 | 2023-11-27 00:15 | 120 | 移3 | 01:00 | 02:00 |
流程4 | 2023-11-24 23:45 | 2023-11-27 00:15 | 1080 | 班次4 | 07:00 | 16:00 |
流程4 | 2023-11-24 23:45 | 2023-11-27 00:15 | 1080 | 转变5 | 12:00 | 21:00 |
参见这个数据库<>小提琴。
我还尝试了一种不同的方法,其中班次和处理日期/时间范围在所有午夜交叉点都分开。虽然达到了相同的结果,但是代码更加复杂。我没有在这里发布该源代码,但已将其包含在上述小提琴的末尾。