I have a table (Events
) with the following columns:
ClientID, EventID, EventName, StartDate, Duration
Consider the following data:
INSERT INTO Events(ClientID, EventID, EventName, StartDate, Duration)
VALUES (1, 1, 'Login', '2016-11-27 1:30:00', 86400000),
(2, 1, 'Login', '2016-11-27 0:30:00', 86400000),
(3, 1, 'Login', '2016-11-27 0:00:00', 86400000)
As you can see, the Duration
is in milliseconds and can span multiple days.
In the end, I would like to return the following for 2016-11-28:
EventID EventName Sum(Duration) Count(ClientID)
--------------------------------------------------
1 'Login' 7200000 2
The question being answered here is "How many clients were logged in on 2016-11-28 and for how many milliseconds?". 86400000 ms is 1 day. So, the first row would have 1.5 hours on 2016-11-28 (1:30 after midnight), the second would have .5 hours (0:30 after midnight), the third would have 0 hours (0:00 after midnight). Add those up, you get 2 hours or 7200000 ms and 2 clients since the 3rd did not have a duration on that day.
I have spent days toying with CASE
statements to try to do this, and I really feel like there has to a better approach.
DECLARE @StartDate as DateTime, @EndDate as DateTime
SET @StartDate = '2016-11-28'
SET @EndDate = '2016-11-29'
SELECT
EventID, EventName, COUNT(ClientID) AS CountClients,
SUM(CAST(DATEDIFF(ms, CASE
WHEN StartDate < @StartDate
THEN @StartDate
ELSE StartDate
END,
CASE
WHEN DATEADD(ms, Duration, StartDate) >= @EndDate
THEN @EndDate
ELSE DATEADD(ms, Duration, StartDate)
END) AS bigint)) AS SumDuration
FROM Events
WHERE
((StartDate >= @StartDate AND StartDate < @EndDate)
OR (DATEADD(ms, Duration, StartDate) >= @StartDate AND DATEADD(ms, Duration, StartDate) < @EndDate))
GROUP BY EventID, EventName
I am really struggling with how to do this efficiently, considering that I will need to get partial durations for dates that span more than a day. Any help would really be appreciated.