I have a table (table1
) which includes columns [deadline]
and [completion]
, both of datatype datetime
.
I need to calculate the date diff in minutes between [deadline]
and [completion]
.
For example:
- deadline = 2018-11-22 09:05
- completion = 2018-11-22 9:10
Result needs to be 5.
However, I also need to factor in bank holidays, using a previously generated custom table, say [tableBH]
, which has [date] [bank holiday name]
.
Also, there is another table a maintenance table [tableMaintenance]
with columns [date] [maintenance reason]
.
Lastly, I working hours monday to friday are 0900 to 1900, and on saturday 1100 – 1500.
I need the datediff, ideally in minutes, to take into account non-working hours, non-working days, and bank holidays.
Ideally by way of a function. Can anyone give pointers as to where to start?
My current script only does DATEDIFF ( mi , deadline, completion)
Well, I say that, but it is currently a little more convoluted, but I can deal with that later.
For transparency, my current code, which does not take into account maintenance days, bank holidays, and non-working hours is:
CONVERT(varchar, Datediff(n, [deadline],[completion])/60) + ':' + Right('0' + CONVERT(varchar,(datediff(n,[deadline],[completion]) %60)),2)) as [TimeDIFF]
This format doesn’t matter too much.
What matters is referencing the
- custom bank holiday table, not sys,
- custom maintenance table,
- working hours (different on a saturday to working days)
- passed through a function (in SQL Server 2017)
Sample:
TableBH – columns [date],[bank holiday name]
26 November 2018, FakeBH1
TableMaintenance – columns [date],[maintenance reason]
28 November 2018, FakeMaintenance1
table1 – columns [id], [deadline], [completion]
1, 2018-11-24 14:55, 2018-11-27 09:05 2, 2018-11-24 15:05, 2018-11-27 09:04 3, 2018-11-27 11:00, 2018-11-27 10:35 4, 2018-11-27 16:50, 2018-11-29 09:10
output id, duration in mins 1 = 10 2 = 4 3 = -25 4 = 20
Advertisement
Answer
BankDate BankTimeStart BankTimeEnd WorkingMins 2018-11-19 09:00:00.0000000 19:00:00.0000000 720 2018-11-20 09:00:00.0000000 19:00:00.0000000 720 2018-11-21 09:00:00.0000000 19:00:00.0000000 720 2018-11-22 09:00:00.0000000 19:00:00.0000000 720 2018-11-23 09:00:00.0000000 19:00:00.0000000 720 2018-11-24 09:00:00.0000000 19:00:00.0000000 480 2018-11-25 09:00:00.0000000 19:00:00.0000000 480 2018-11-26 09:00:00.0000000 19:00:00.0000000 480 *(Bank Holiday)* 2018-11-27 09:00:00.0000000 19:00:00.0000000 720 2018-11-28 00:00:00.0000000 00:00:00.0000000 0 *(fakeMaintenance)* 2018-11-29 09:00:00.0000000 19:00:00.0000000 720 2018-11-30 09:00:00.0000000 19:00:00.0000000 720
Above table inspired by @donPablo
Actually, Bank holidays and Sundays are now 9am to 5pm.
There are are also 2 additional columns [IsBankHoliday] and [IsNonWorkingDay] (generally maintenance).
Then I added the following to the DimDate procedure:
CASE WHEN [nw].[NonWorkingDayDate] IS NOT NULL THEN '00:00:00' ELSE '09:00:00' END AS [StartTime], CASE WHEN [nw].[NonWorkingDayDate] IS NOT NULL THEN '00:00:00' WHEN [bh].[BankHolidayDate] IS NOT NULL or [DayOfWeekNumber] IN (6,7) THEN '17:00:00' ELSE '21:00:00' END AS [EndTime],
Then, I created the following function:
ALTER FUNCTION [Udf].[GenerateBreachTime] (@CompletionAt [datetime],
@Deadline [datetime])
SELECT (SELECT CASE WHEN @Deadline > @CompletionAt THEN -1 WHEN CONVERT(varchar(8), @Deadline, 112) <= CONVERT(varchar(8), @CompletionAt, 112) THEN CASE --NWD DC|| ||DC = 0 WHEN ([Non Working Day] = 1 AND CONVERT(varchar(8), @Deadline, 112) = CONVERT(varchar(8), [DateId], 112)) --nwd OR @Deadline > CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime) --deadline after hours OR @CompletionAt < CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime) THEN 0 -- D|C| WHEN @Deadline < CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime) AND @CompletionAt < CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime) THEN DATEDIFF(n, CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime), @CompletionAt) --D||C WHEN @Deadline < CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime) AND @CompletionAt > CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime) THEN DATEDIFF(n, CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime), CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime)) -- |DC| WHEN @Deadline >= CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime) AND @CompletionAt <= CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime) THEN DATEDIFF(n, @Deadline, @CompletionAt) --|D|C WHEN @Deadline >= CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime) AND @CompletionAt > CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime) THEN DATEDIFF(n, @Deadline, CAST(Concat(CONVERT(date, @Deadline), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime)) ELSE 0 END ELSE 0 END AS [BreachMins] FROM [Vw].[Dim Date] DimDate WHERE dateid = CONVERT(varchar(8), @Deadline, 112)) + (SELECT CASE WHEN CONVERT(varchar(8), @Deadline, 112) >= CONVERT(varchar(8), @CompletionAt, 112) THEN 0 WHEN CONVERT(varchar(8), @Deadline, 112) <= CONVERT(varchar(8), @CompletionAt, 112) THEN CASE -- C|| OR NWD WHEN @CompletionAt < CAST(Concat(CONVERT(date, @CompletionAt), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime) OR ([Non Working Day] = 1 AND CONVERT(varchar(8), @CompletionAt, 112) = CONVERT(varchar(8), [DateId], 112)) THEN 0 -- |C| WHEN @CompletionAt <= CAST(Concat(CONVERT(date, @CompletionAt), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime) --THEN 1 THEN DATEDIFF(n, CAST(Concat(CONVERT(date, @CompletionAt), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime), @CompletionAt) --||C WHEN @CompletionAt > CAST(Concat(CONVERT(date, @CompletionAt), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime) --THEN 2 THEN DATEDIFF(n, CAST(Concat(CONVERT(date, @CompletionAt), ' ', CONVERT(char(5), [StartTime], 108)) AS datetime), CAST(Concat(CONVERT(date, @CompletionAt), ' ', CONVERT(char(5), [EndTime], 108)) AS datetime)) ELSE 0 END ELSE 0 END AS [BreachMins] FROM [Vw].[Dim Date] DimDate WHERE dateid = CONVERT(varchar(8), @CompletionAt, 112)) + (SELECT CASE WHEN SUM(workingminsinday) IS NULL THEN 0 ELSE SUM(workingminsinday) END AS [x] FROM vw.[Dim Date] WHERE dateid BETWEEN CONVERT(varchar(8), @Deadline + 1, 112) AND CONVERT(varchar(8), @CompletionAt - 1, 112)) AS [BreachMins]
The daily procedure calls the function
,[BreachTime].[BreachInMins] FROM [Syn].[X] CROSS APPLY [udf].[GenerateBreachTime] ([Completed],[Deadline]) [BreachTime]
The subsequent view handles -1 cases, changing them to NULL.
I hope this helps someone else, and apologies if not entirely clear.