我想通过减去工作开始时间和工作结束时间之间的休息时间来计算实际工作时间。
我所拥有的:
•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
语句进行检查,则有很多可能性。
我可以知道有没有更聪明或更简单的方法来检查和计算实际工作时间?
答案 0 :(得分:0)
如果没有示例输入/输出,很难说,但是基本的查询仍然是:
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 :(得分:0)
这是我解决该问题的方法:
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,并使处理此问题变得更加困难-那不是'不在您的问题中描述,因此希望这不是问题!此代码还可扩展到诸如通过使用partition by
子句中的over
语句按用户ID或日期分组的情况。