SQL Server:条件语句逻辑

SQL Server:条件语句逻辑,sql,sql-server,conditional-statements,qsqlquery,Sql,Sql Server,Conditional Statements,Qsqlquery,我想通过扣除工作开始时间和工作结束时间内的休息时间来计算实际工作时间 我所拥有的: •Work Start Time (10:10:00) •Work End Time (14:10:00) •Break 1 Start Time (10:00:00) •Break 1 End Time (10:15:00) •Break 2 Start Time (14:00:00) •Break 2 End Time (14:30:00) •Break 3 Start Time (17:45:00) •Br

我想通过扣除工作开始时间和工作结束时间内的休息时间来计算实际工作时间

我所拥有的:

•Work Start Time (10:10:00)
•Work End Time (14:10:00)
•Break 1 Start Time (10:00:00)
•Break 1 End Time (10:15:00)
•Break 2 Start Time (14:00:00)
•Break 2 End Time (14:30:00)
•Break 3 Start Time (17:45:00)
•Break 3 End Time (18:00:00)
但有许多条件,例如:

  • 工作开始时间可能在休息1开始时间之前开始,然后工作结束时间在休息1结束时间之前结束

  • 工作开始时间可能在休息1开始时间之前开始,然后工作结束时间在休息2开始时间之前结束

  • 工作开始时间可能在休息2开始时间之前开始,但在休息1结束时间之后,工作结束时间可能在休息3结束时间之前结束

等等

如果我写
if-Else
语句来检查,有很多可能性


我可以知道有没有更聪明或更简单的方法来检查和计算实际工作时间?

如果没有您的示例输入/输出,很难判断,但基本的查询是:

根据条件更新

SELECT CASE WHEN Break 1 Start Time > Work Start Time THEN
                  CONVERT(varchar(5), DATEADD(minute, DATEDIFF(minute, Work Start Time, Work End Time) - (DATEDIFF(minute, Break 1 Start Time, Break 1 End Time) + DATEDIFF(minute, Break 2 Start Time, Break 2 End Time) +               DATEDIFF(minute, Break 3 Start Time, Break 3 End Time)), 0), 114) 
            WHEN  Break 2 Start Time > Work Start Time AND Break 1 END Time > Work Start Time AND  THEN 
                  CONVERT(varchar(5), DATEADD(minute, DATEDIFF(minute, Work Start Time, Work End Time) - (DATEDIFF(minute, Break 2 Start Time, Break 2 End Time) +               DATEDIFF(minute, Break 3 Start Time, Break 3 End Time)), 0), 114) 
            WHEN  Break 3 Start Time > Work Start Time AND Break 2 END Time > Work Start Time AND  THEN 
                  CONVERT(varchar(5), DATEADD(minute, DATEDIFF(minute, Work Start Time, Work End Time) - (DATEDIFF(minute, Break 3 Start Time, Break 3 End Time)), 0), 114) 
            ELSE '00:00' END AS [Working Hours]    



/* Results: 03:00 */

以下是我对这个问题的解决方案:

select sum(TimeInCurrentEvent) -- in minutes
from (
  select
   sum(case when EventType = 'Work Start Time' then 1 
        when EventType = 'Work End Time' then -1 end) over (order by TimeOfDay) as InWorkSpan
   ,sum(case when EventType = 'Break Start Time' then 1 
        when EventType = 'Break End Time' then -1 end) over (order by TimeOfDay) as InBreakSpan
   ,datediff(minute, TimeOfDay, Lead(TimeOfDay, 1, null) over (order by TimeOfDay)) as TimeInCurrentEvent
   ,*
   from WorkEvents
) a
where InWorkSpan = 1 and InBreakSpan = 0

我在这里使用的最大技巧是(…)行上的两个
和(…)。如果当前事件是“工作开始”或在下一个“工作结束”之前,则第一个将返回1,否则返回0。第二个类似,只是它会跟踪打断。使用这两个值,我们可以使用末尾的
where
子句过滤到介于工作开始和结束之间、但在中断开始和结束之外的事件集

之后,唯一剩下的事情就是确定每个事件之间花费的时间量。为了做到这一点,我使用了(…)上方的
引导(…)
将下一行的事件时间拉到上一行,以便进行比较。将我们筛选出来的行相加,只会得到工作时间和休息时间之外的时间

如果您想进一步了解这个查询的工作原理,我建议只运行子查询来查看窗口函数返回的内容


请注意,一些无效数据(一行有两个工作结束时间或以一个工作结束时间开始)将导致上述总和不返回0或1,并使处理此问题变得更加困难-这在您的问题中没有描述,因此我希望这不是一个问题!此代码还可扩展到以下情况:使用
over
子句中的
partition by
语句按用户id分组或按日期分组。

有3个固定中断时间?请添加一些样本数据。嗨,我已经用一些样本数据编辑了我的问题。到目前为止,你尝试了什么?如果您添加一个参数,您的查询只能计算我的工作开始时间是否在中断1开始之前,工作结束时间是否在中断3结束之后,这会有所帮助。如果工作开始时间在第二次休息开始之前,工作结束时间在第三次休息开始之前,那又如何呢?不。它不会在意
第1次休息开始时间
工作结束时间
,它只会将所有休息时间相加,并从工作时间中扣除。你可以检查这里提到的情况,我的意思是,如果工作时间不包括一整天,它就不起作用了。您的查询是所有3个休息时间的总和,并从工作时间中减去它们。但是,如果工作时间从10:45:00开始(休息1结束后)?我可以看到您的查询只是检查工作开始时间。它无法确定工作开始时间和工作结束时间之间的中断次数。那么,它如何计算实际的工作时间呢?嗨,sjager,我真的很感谢你的努力。我花了一些时间来理解你的询问和解释。但我真的无法理解你询问的逻辑。我尝试运行它,但我不确定为eventType放什么。我的表格只包含:部门(HR、IT)、班次(白天或晚上)、开始日期、结束日期(工作时间表的生效日期)、休息1、休息1持续时间。。。休息3分钟。我需要通过将break N duration添加到break N来确定我的break N结束时间。如果您能花些时间帮助检查,我真的很感激。
select sum(TimeInCurrentEvent) -- in minutes
from (
  select
   sum(case when EventType = 'Work Start Time' then 1 
        when EventType = 'Work End Time' then -1 end) over (order by TimeOfDay) as InWorkSpan
   ,sum(case when EventType = 'Break Start Time' then 1 
        when EventType = 'Break End Time' then -1 end) over (order by TimeOfDay) as InBreakSpan
   ,datediff(minute, TimeOfDay, Lead(TimeOfDay, 1, null) over (order by TimeOfDay)) as TimeInCurrentEvent
   ,*
   from WorkEvents
) a
where InWorkSpan = 1 and InBreakSpan = 0