As a follow-up to an earlier post (Return a list of all dates between a start and end date), I need to find the number of working hours between two timestamps – in this case, it was to see how long a support ticket had been open before it was initially assigned, but the user didn’t want non-work hours to count against them.
To do this, I used the previous script to generate a list of dates and hours, and then marked the rows as work time or not (based on day of week and hour of day, evaluated together). The result was a table that would effectively let me do a SUM to find the value I was looking for. Once I had that table, I could join to it for rows between the two datetimes in question and SUM up rows that had “WorkTime” marked:
SELECT tt.TicketNumber, tt.TicketCreateTime, tt.TicketAssignTime, SUM( CONVERT(INT, wh.WorkTime)) as WorkHoursBeforeAssigned COUNT(CONVERT(INT, wh.WorkTime)) as TotalHoursBeforeAssigned FROM TroubleTickets tt JOIN #WorkingHours wh ON wh.EvaluateTime BETWEEN tt.TicketCreateTime AND tt.TicketAssignTime GROUP BY tt.TicketNumber, tt.TicketCreateTime, tt.TicketAssignTime
In this case, tickets that were created and picked up after hours, without passing any worktime, would show as zero hours old (as they should, since they were interested in working time) – however, I’ve also included COUNT here to show total hours as well as work hours.
Also, this script only counts for raw day-of-week and hour-of-day working time – it ignores holidays and other special circumstances. I have a script that tracks holidays (American ones, at least), and I’ll put that up shortly as well – if you want to take holidays into account, you could incorporate that into your evaluation.
Here’s the script that builds the working time table (you can also download it here):
-- Set things up before we get started -------------------------------------- DECLARE @WorkTimeStart TINYINT, @WorkTimeEnd TINYINT, @WorkDayOfWeekStart TINYINT, @WorkDayOfWeekEnd TINYINT DECLARE @StartDate DATETIME, @EndDate DATETIME CREATE TABLE #WorkingHours ( EvaluateTime DATETIME, IsWorktime BIT DEFAULT(0) ) -------------------------------------- SET @WorkTimeStart = 7 --7AM SET @WorkTimeEnd = 16 --4PM hour (4-5PM count as working) SET @WorkDayOfWeekStart = 2 --Monday SET @WorkDayOfWeekEnd = 6 --Friday SET @StartDate = '2000-01-01 00:00:00' SET @EndDate = '2020-12-31 23:59:59' -------------------------------------- -- Built the list of timestamps we're working with ;WITH numberlist(number) AS (SELECT RANK() over(order by c1.object_id, c1.column_id, c2.object_id, c2.column_id) from sys.columns c1 cross join sys.columns c2) INSERT INTO #WorkingHours (EvaluateTime) SELECT DATEADD(hh, number-1, @StartDate) FROM numberlist WHERE DATEADD(hh, number-1, @StartDate) <= @EndDate -- Set the times to worktime if they match criteria UPDATE #WorkingHours SET IsWorktime = CASE WHEN (DATEPART(dw, EvaluateTime) BETWEEN @WorkDayOfWeekStart AND @WorkDayOfWeekEnd) AND (DATEPART(hh, EvaluateTime) BETWEEN @WorkTimeStart AND @WorkTimeEnd) THEN 1 ELSE 0 END -- Retun the results SELECT * FROM #WorkingHours ORDER BY EvaluateTime DROP TABLE #WorkingHours
great! didactic and very well explained