【问题标题】:SQL Server: Conditional Statement LogicSQL Server:条件语句逻辑
【发布时间】:2020-03-22 14:40:49
【问题描述】:

我想通过在工作开始时间和工作结束时间中减去休息时间来计算实际工作时间。

我有什么:

•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 语句来检查,有很多可能性。

请问有没有更聪明或更简单的方法来检查和计算实际工作时间?

【问题讨论】:

  • 固定休息时间有3个?请添加一些示例数据。
  • 您好,我已经用一些示例数据编辑了我的问题。
  • 到目前为止您尝试过什么?如果您添加 Minimum Reproducible Example 会有所帮助

标签: sql sql-server conditional-statements qsqlquery


【解决方案1】:

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

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

http://sqlfiddle.com/#!18/5f345/12

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

之后,唯一剩下的就是确定每个事件之间花费的时间量。为此,我使用lead(...) over (...) 将下一个事件时间拉到上一行以进行比较。将我们过滤到的行加起来只会得到花在工作内部和休息时间之外的时间。

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

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

【讨论】:

  • 嗨 sjager,非常感谢您的努力。我花了一些时间来了解您的查询和解释。但我无法真正理解您查询中的逻辑。我尝试运行它,但我不确定要为 eventType 放置什么。我的表只包含:部门(HR,IT),班次(白天或晚上),开始日期,结束日期(工作时间表的有效日期),休息1,休息1持续时间......休息3持续时间。我需要通过将 break N 持续时间添加到 break N 来确定我的 break N 结束时间。如果您能花一些时间帮助检查一下,我真的很感激。
【解决方案2】:

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

FIDDLE DEMO

SELECT 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) 


/* Results: 03:00 */

根据条件更新

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 */

【讨论】:

  • 您的查询只能计算我的工作开始时间是否在休息 1 开始之前和工作结束时间在休息 3 结束之后。如果工作开始时间在休息 2 开始之前,工作结束时间在休息 3 开始之前呢?
  • 没有。它不会打扰Break 1 StartWork End Time,它只会将所有的休息时间相加并从工作时间中扣除。您可以在此处查看上述案例FIDDLE DEMO
  • 我的意思是如果工作时间没有覆盖一整天,它就不起作用。您的查询是所有 3 个休息时间的总和,并从工作时间中减去它们。但是如果工作时间从 10:45:00(休息 1 结束后)开始呢?
  • 我可以看到您的查询仅检查工作开始时间。它无法确定工作开始时间和工作结束时间之间有多少休息时间。那么如何计算实际工作时间呢?
猜你喜欢
  • 1970-01-01
  • 2014-12-18
  • 2021-10-24
  • 2016-10-01
  • 1970-01-01
  • 2021-12-08
  • 2019-08-15
  • 2011-11-27
  • 1970-01-01
相关资源
最近更新 更多