我需要计算两个日期之间的DateDiff(小时),但仅限于营业时间(8:30 - 16:00,没有周末).然后根据下面的示例将此结果放入Reaction_Time列.
ID Date Reaction_Time Overdue 1 29.04.2003 15:00:00 1 30.04.2003 11:00:00 3:30 2 30.04.2003 14:00:00 2 01.05.2003 14:00:00 7:30 YES
*注意:我没有检查示例中的日期是否是假日.
我正在使用SQL Server 2005
这将与更大的查询相结合,但是现在我需要的是这个开始,我将试图弄清楚如何将它们全部放在一起.谢谢您的帮助!
编辑:嘿,谢谢大家的回复.但是由于SQL方面的解决方案明显很复杂,我们决定在Excel中执行此操作,而不管报告将在何处移动.不好意思,但我真的认为这比这更简单.事实上,我们没有时间.
我建议建立一个用户定义的函数,根据您的规则计算营业时间的日期差异.
SELECT Id, MIN(Date) DateStarted, MAX(Date) DateCompleted, dbo.udfDateDiffBusinessHours(MIN(Date), MAX(Date)) ReactionTime FROM Incident GROUP BY Id
我不确定你的Overdue
价值来自哪里,所以我把它留在了我的例子中.
在函数中,您可以编写比查询更具表现力的SQL,并且不会使用业务规则阻塞查询,从而难以维护.
此外,功能可以轻松重复使用.扩展它以包括对假期的支持(我在Holidays
这里考虑一个表)不会太难.可以进行进一步的改进,而无需更改难以读取的嵌套SELECT/CASE WHEN构造,这将是替代方案.
如果我今天有时间,我会考虑编写一个示例函数.
编辑:这里有花里胡哨的东西,透明地计算周末:
ALTER FUNCTION dbo.udfDateDiffBusinessHours ( @date1 DATETIME, @date2 DATETIME ) RETURNS DATETIME AS BEGIN DECLARE @sat INT DECLARE @sun INT DECLARE @workday_s INT DECLARE @workday_e INT DECLARE @basedate1 DATETIME DECLARE @basedate2 DATETIME DECLARE @calcdate1 DATETIME DECLARE @calcdate2 DATETIME DECLARE @cworkdays INT DECLARE @cweekends INT DECLARE @returnval INT SET @workday_s = 510 -- work day start: 8.5 hours SET @workday_e = 960 -- work day end: 16.0 hours -- calculate Saturday and Sunday dependent on SET DATEFIRST option SET @sat = CASE @@DATEFIRST WHEN 7 THEN 7 ELSE 7 - @@DATEFIRST END SET @sun = CASE @@DATEFIRST WHEN 7 THEN 1 ELSE @sat + 1 END SET @calcdate1 = @date1 SET @calcdate2 = @date2 -- @date1: assume next day if start was after end of workday SET @basedate1 = DATEADD(dd, 0, DATEDIFF(dd, 0, @calcdate1)) SET @calcdate1 = CASE WHEN DATEDIFF(mi, @basedate1, @calcdate1) > @workday_e THEN @basedate1 + 1 ELSE @calcdate1 END -- @date1: if Saturday or Sunday, make it next Monday SET @basedate1 = DATEADD(dd, 0, DATEDIFF(dd, 0, @calcdate1)) SET @calcdate1 = CASE DATEPART(dw, @basedate1) WHEN @sat THEN @basedate1 + 2 WHEN @sun THEN @basedate1 + 1 ELSE @calcdate1 END -- @date1: assume @workday_s as the minimum start time SET @basedate1 = DATEADD(dd, 0, DATEDIFF(dd, 0, @calcdate1)) SET @calcdate1 = CASE WHEN DATEDIFF(mi, @basedate1, @calcdate1) < @workday_s THEN DATEADD(mi, @workday_s, @basedate1) ELSE @calcdate1 END -- @date2: assume previous day if end was before start of workday SET @basedate2 = DATEADD(dd, 0, DATEDIFF(dd, 0, @calcdate2)) SET @calcdate2 = CASE WHEN DATEDIFF(mi, @basedate2, @calcdate2) < @workday_s THEN @basedate2 - 1 ELSE @calcdate2 END -- @date2: if Saturday or Sunday, make it previous Friday SET @basedate2 = DATEADD(dd, 0, DATEDIFF(dd, 0, @calcdate2)) SET @calcdate2 = CASE DATEPART(dw, @calcdate2) WHEN @sat THEN @basedate2 - 0.00001 WHEN @sun THEN @basedate2 - 1.00001 ELSE @date2 END -- @date2: assume @workday_e as the maximum end time SET @basedate2 = DATEADD(dd, 0, DATEDIFF(dd, 0, @calcdate2)) SET @calcdate2 = CASE WHEN DATEDIFF(mi, @basedate2, @calcdate2) > @workday_e THEN DATEADD(mi, @workday_e, @basedate2) ELSE @calcdate2 END -- count full work days (subtract Saturdays and Sundays) SET @cworkdays = DATEDIFF(dd, @basedate1, @basedate2) SET @cweekends = @cworkdays / 7 SET @cworkdays = @cworkdays - @cweekends * 2 -- calculate effective duration in minutes SET @returnval = @cworkdays * (@workday_e - @workday_s) + @workday_e - DATEDIFF(mi, @basedate1, @calcdate1) + DATEDIFF(mi, @basedate2, @calcdate2) - @workday_e -- return duration as an offset in minutes from date 0 RETURN DATEADD(mi, @returnval, 0) END
该函数返回一个DATETIME
值,表示从日期0(即"1900-01-01 00:00:00"
)的偏移量.因此,例如8:00小时的时间跨度将是"1900-01-01 08:00:00"
25小时"1900-01-02 01:00:00"
.函数的结果是时间在业务差异两个日期之间的时间.没有特殊处理/支持加班.
SELECT dbo.udfDateDiffBusinessHours('2003-04-29 15:00:00', '2003-04-30 11:00:00') --> 1900-01-01 03:30:00.000 SELECT dbo.udfDateDiffBusinessHours('2003-04-30 14:00:00', '2003-05-01 14:00:00') --> 1900-01-01 07:30:00.000
该功能假定下一个可用工作日(08:30 h)开始于非工作@date1
时间,而上一个可用工作日(16:00 h)结束时为非工作@date2
时间.
"下一个/上一个可用"是指:
如果@date1
是'2009-02-06 07:00:00'
(星期五),它将成为'2009-02-06 08:30:00'
(星期五)
如果@date1
是'2009-02-06 19:00:00'
(星期五),它将成为'2009-02-09 08:30:00'
(星期一)
如果@date2
是'2009-02-09 07:00:00'
(星期一),它将成为'2009-02-06 16:00:00'
(星期五)
如果@date2
是'2009-02-09 19:00:00'
(星期一),它将成为'2009-02-09 16:00:00'
(星期一)
DECLARE @BusHourStart DATETIME, @BusHourEnd DATETIME SELECT @BusHourStart = '08:30:00', @BusHourEnd = '16:00:00' DECLARE @BusMinutesStart INT, @BusMinutesEnd INT SELECT @BusMinutesStart = DATEPART(minute,@BusHourStart)+DATEPART(hour,@BusHourStart)*60, @BusMinutesEnd = DATEPART(minute,@BusHourEnd)+DATEPART(hour,@BusHourEnd)*60 DECLARE @Dates2 TABLE (ID INT, DateStart DATETIME, DateEnd DATETIME) INSERT INTO @Dates2 SELECT 1, '15:00:00 04/29/2003', '11:00:00 04/30/2003' UNION SELECT 2, '14:00:00 04/30/2003', '14:00:00 05/01/2003' UNION SELECT 3, '14:00:00 05/02/2003', '14:00:00 05/06/2003' UNION SELECT 4, '14:00:00 05/02/2003', '14:00:00 05/04/2003' UNION SELECT 5, '07:00:00 05/02/2003', '14:00:00 05/02/2003' UNION SELECT 6, '14:00:00 05/02/2003', '23:00:00 05/02/2003' UNION SELECT 7, '07:00:00 05/02/2003', '08:00:00 05/02/2003' UNION SELECT 8, '22:00:00 05/02/2003', '23:00:00 05/03/2003' UNION SELECT 9, '08:00:00 05/03/2003', '23:00:00 05/04/2003' UNION SELECT 10, '07:00:00 05/02/2003', '23:00:00 05/02/2003' -- SET DATEFIRST to U.S. English default value of 7. SET DATEFIRST 7 SELECT ID, DateStart, DateEnd, CONVERT(VARCHAR, Minutes/60) +':'+ CONVERT(VARCHAR, Minutes % 60) AS ReactionTime FROM ( SELECT ID, DateStart, DateEnd, Overtime, CASE WHEN DayDiff = 0 THEN CASE WHEN (MinutesEnd - MinutesStart - Overtime) > 0 THEN (MinutesEnd - MinutesStart - Overtime) ELSE 0 END WHEN DayDiff > 0 THEN CASE WHEN (StartPart + EndPart - Overtime) > 0 THEN (StartPart + EndPart - Overtime) ELSE 0 END + DayPart ELSE 0 END AS Minutes FROM( SELECT ID, DateStart, DateEnd, DayDiff, MinutesStart, MinutesEnd, CASE WHEN(@BusMinutesStart - MinutesStart) > 0 THEN (@BusMinutesStart - MinutesStart) ELSE 0 END + CASE WHEN(MinutesEnd - @BusMinutesEnd) > 0 THEN (MinutesEnd - @BusMinutesEnd) ELSE 0 END AS Overtime, CASE WHEN(@BusMinutesEnd - MinutesStart) > 0 THEN (@BusMinutesEnd - MinutesStart) ELSE 0 END AS StartPart, CASE WHEN(MinutesEnd - @BusMinutesStart) > 0 THEN (MinutesEnd - @BusMinutesStart) ELSE 0 END AS EndPart, CASE WHEN DayDiff > 1 THEN (@BusMinutesEnd - @BusMinutesStart)*(DayDiff - 1) ELSE 0 END AS DayPart FROM ( SELECT DATEDIFF(d,DateStart, DateEnd) AS DayDiff, ID, DateStart, DateEnd, DATEPART(minute,DateStart)+DATEPART(hour,DateStart)*60 AS MinutesStart, DATEPART(minute,DateEnd)+DATEPART(hour,DateEnd)*60 AS MinutesEnd FROM ( SELECT ID, CASE WHEN DATEPART(dw, DateStart) = 7 THEN DATEADD(SECOND, 1, DATEADD(DAY, DATEDIFF(DAY, 0, DateStart), 2)) WHEN DATEPART(dw, DateStart) = 1 THEN DATEADD(SECOND, 1, DATEADD(DAY, DATEDIFF(DAY, 0, DateStart), 1)) ELSE DateStart END AS DateStart, CASE WHEN DATEPART(dw, DateEnd) = 7 THEN DATEADD(SECOND, -1, DATEADD(DAY, DATEDIFF(DAY, 0, DateEnd), 0)) WHEN DATEPART(dw, DateEnd) = 1 THEN DATEADD(SECOND, -1, DATEADD(DAY, DATEDIFF(DAY, 0, DateEnd), -1)) ELSE DateEnd END AS DateEnd FROM @Dates2 )Weekends )InMinutes )Overtime )Calculation