简体   繁体   English

如何 select 与固定时间范围 (B) 重叠的时间范围 (A),其中 A 的开始时间或结束时间超出 SQL 服务器中的 B 范围

[英]How to select the time range (A) that overlaps a fixed time range (B) wherein A's Start Time or End Time falls outside the range of B in SQL Server

This problem corresponds to a real-world scenario wherein an employee is given a shift schedule (ex. 8am-5pm).这个问题对应于一个现实世界的场景,其中给员工一个轮班时间表(例如上午 8 点到下午 5 点)。 In this shift, an employee is entitled to have one or more fixed breaks (ex. 10am-10:30am & 3pm-30:30pm).在此轮班中,员工有权享有一个或多个固定休息时间(例如上午 10 点至上午 10:30 和下午 3 点至下午 30:30)。 If an employee clocks out before 10am or after 10:30 am (ex. 9:45am-10:20am or 10:20am-10:40am), he/she will get a penalty deduction, called mid-breaks , for clocking out for breaks that are not in schedule.如果员工在上午 10 点之前或 10:30 之后(例如 9:45am-10:20am 或 10:20am-10:40am)下班,他/她将获得罚金扣除,称为mid-breaks ,用于打卡对于不在计划中的休息时间。

Scheduled Fixed Breaks:预定的固定休息时间:

Start Time     End Time
------------------------
10:00 am       10:30 am
03:00 pm       03:30 pm

Time Logs:时间日志:

Time          Type
---------------------
08:00 am      IN
10:00 am      OUT
10:40 am      IN
12:00 pm      OUT (lunch break)
01:00 pm      IN  (lunch break)
02:45 pm      OUT
03:30 pm      IN
04:15 PM      OUT
04:25 PM      IN
05:00 pm      OUT

Expected Output预计 Output

Actual Break Start    Actual Break End    Mid-break (penalty)    Fixed Break violated
---------------------------------------------------------------------------------
10:00 am              10:40 am            10 minutes             10:00 am - 10:30 am
02:45 pm              03:30 pm            15 minutes             03:00 pm - 03:30 pm
04:15 pm              04:25 pm            10 minutes             03:00 pm - 03:30 pm
  1. The 1st mid-break is 10 minutes because the 10:40 am clock out is beyond the 10:00 am - 10:30 am break.第一个中间休息时间为 10 分钟,因为10:40 am的打卡时间超出了上午 10:00 - 上午 10:30 的休息时间。
  2. The 2nd mid-break is 15 minutes because the employee clocked out 15 minutes before the scheduled 10:00 am - 10:30 am break.第二次中间休息时间为 15 分钟,因为员工在预定的上午 10:00 至上午 10:30 休息时间前 15 分钟下班。
  3. The last mid-break penalty took 10 minutes because the employee clocked out in neither of the scheduled breaks.最后一次中间休息时间被罚了 10 分钟,因为该员工在预定的休息时间都没有打卡。

Note: To make things simpler, AM time logs will be validated only against the AM breaks and PM time logs will be validate only against PM breaks.注意:为简单起见,AM 时间日志将仅针对 AM 休息时间进行验证,而 PM 时间日志将仅针对 PM 休息时间进行验证。 So the 2:45 breaks will not violate the break schedule for 10am.因此,2:45 的休息时间不会违反上午 10 点的休息时间安排。

I was only able to pair INs and OUTs (as shown in the example below) but don't know how query the mid-breaks as explained above.我只能配对 IN 和 OUT(如下面的示例所示),但不知道如何如上所述查询中间休息时间。

Sample query to pair INs and OUTs:用于配对 IN 和 OUT 的示例查询:

SELECT
    CASE WHEN t.LogType = 'CheckOut' THEN NULL ELSE t.CheckTime END [Time IN],
    CASE WHEN t.LogType = 'CheckOut' THEN t.CheckTime ELSE x.CheckTime END [Time Out]
FROM TimeLogs t
OUTER APPLY
(
    SELECT TOP 1 Id, CheckTime, LogType
    FROM TimeLogs
    WHERE t.CheckTime < CheckTime
    AND t.LogType <> LogType
    AND t.EmployeeId = EmployeeId
    ORDER BY CAST(CheckTime AS TIME)
) x
WHERE t.EmployeeId = @EmployeeId
    AND (t.LogType = 'CheckIn' )
ORDER BY CAST(t.CheckTime AS TIME)
Time In     Time Out  
---------------------- 
08:00 am    10:00 am
10:40 am    12:00 pm
01:00 pm    02:45 pm
03:30 pm    04:15 pm
04:25 pm    05:00 pm

The only remaining problem for me now is how to query the expected output (see above expected output table above) to calculate the mid-breaks.现在对我来说唯一剩下的问题是如何查询预期的 output(参见上面的预期 output 表)来计算中间休息时间。 Any help from anyone is highly appreciated.非常感谢任何人的任何帮助。 Thanks!谢谢!

So, here a solution I found out working.所以,这里有一个我发现有效的解决方案。 However, it might require additional fixes if you encounter - as mentioned before - incorrect logging like sequential logging of one type etc. ...但是,如果您遇到 - 如前所述 - 不正确的日志记录,例如一种类型的顺序日志记录等,则可能需要额外的修复......

According to your example, the lunch break was accepted even if it was not included in the breaks table - I added it... you might hard-code this if required.根据您的示例,即使午休时间未包含在休息表中,也已接受午休时间-我添加了它...如果需要,您可以对其进行硬编码。

DECLARE @tBreaks TABLE(
ID int,
StartTime time,
EndTime time
)

INSERT INTO @tBreaks VALUES(1,'10:00:00','10:30:00'),(2,'15:00:00','15:30:00'), (3,'12:00:00','13:00:00')

DECLARE @tTimeLog TABLE(
lTime TIME,
lType NVARCHAR(5)
)

INSERT INTO @tTimeLog VALUES
('08:00:00', 'IN')
,('10:00:00', 'OUT')
,('10:40:00', 'IN')
,('12:00:00', 'OUT')
,('13:00:00', 'IN')
,('14:45:00', 'OUT')
,('15:30:00', 'IN')
,('16:15:00', 'OUT')
,('16:25:00', 'IN')
,('17:00:00', 'OUT')

;WITH cteBreaks AS(
SELECT *, ROW_NUMBER() OVER (ORDER BY StartTime DESC) rn
  FROM @tBreaks
),
cteIn AS(
SELECT lTime AS TIn, LEAD(lTime) OVER (ORDER BY lTime) NextTIn
  FROM @tTimeLog
  WHERE lType = 'IN'
),
cteOut AS(
SELECT lTime AS TOut
  FROM @tTimeLog
  WHERE lType = 'OUT'
),
cteLogTimes AS(
SELECT i.Tin, o.TOut, i.NextTIn
  FROM cteIn i
  LEFT JOIN cteOut o ON o.TOut > i.TIn AND o.TOut <= ISNULL(i.NextTIn, o.TOut)
)
SELECT CONVERT(varchar(15), l.TOut, 100) AS ActualBreakStart
       ,CONVERT(varchar(15), l.NextTIn, 100) AS ActualBreakEnd
       ,CASE WHEN l.TOut < t2.StartTime THEN DATEDIFF(MINUTE, l.TOut, t2.StartTime) ELSE 0 END +
           CASE WHEN l.TOut <= t2.StartTime AND l.NextTIn > t2.EndTime THEN DATEDIFF(MINUTE, t2.EndTime, l.NextTIn) ELSE 0 END +
           CASE WHEN l.TOut > t2.EndTime AND t2.rn = 1 THEN DATEDIFF(MINUTE, l.TOut, l.NextTIn) ELSE 0 END AS Penalty
       ,CONCAT(CONVERT(varchar(15), t2.StartTime), ' - ', CONVERT(varchar(15), t2.EndTime)) AS FixedBreakViolated
  FROM cteLogTimes l
  LEFT JOIN cteBreaks t ON l.TOut >= t.StartTime AND l.NextTIn <= t.EndTime
  LEFT JOIN cteBreaks t2 ON (l.TOut BETWEEN t2.StartTime AND t2.EndTime) OR (l.NextTIn BETWEEN t2.StartTime AND t2.EndTime) OR (l.TOut > t2.EndTime AND t2.rn = 1) OR (l.TOut < t2.StartTime AND l.NextTIn > t2.EndTime)
  WHERE l.NextTIn IS NOT NULL
    AND t.ID IS NULL

The result looks like this:结果如下所示:

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM