Search code examples
sqlsql-serversql-server-2016

Calculating duration between two times that occur within a specific window


I'm trying to calculate the duration between a START_TIME and an END_TIME where they occur within set windows of time with SQL Server 2016.

I have a set of data that looks like this:

| USER| START_TIME       | END_TIME         | DURATION | WINDOW_1_START | WINDOW_1_END | WINDOW_2_START | WINDOW_2_END |
|-----|------------------|------------------|----------|----------------|--------------|-----------------|--------------|
| jim | 2021-01-24 14:00 | 2021-01-24 16:00 | 120      | 17:00          | 20:00        | 20:00           | 22:00        |
| jim | 2021-01-24 16:00 | 2021-01-24 18:00 | 120      | 17:00          | 20:00        | 20:00           | 22:00        | 
| jim | 2021-01-24 18:00 | 2021-01-24 19:30 | 90       | 17:00          | 20:00        | 20:00           | 22:00        |
| jim | 2021-01-24 19:30 | 2021-01-24 22:00 | 150      | 17:00          | 20:00        | 20:00           | 22:00        |

The above output can be achieved with the following query:

SELECT USER,
       START_TIME,
       END_TIME,
       DATEDIFF ( minute, START_TIME, END_TIME ) AS DURATION
       CAST ( '17:00' AS TIME ) AS WINDOW_1_START,
       CAST ( '20:00' AS TIME ) AS WINDOW_1_END,
       CAST ( '20:00' AS TIME ) AS WINDOW_2_START,
       CAST ( '22:00' AS TIME ) AS WINDOW_2_END
FROM EVENTS
;

I want to calculate the duration for each row that occurs within WINDOW_1 and WINDOW_2. My desired output would add WINDOW_1_DURATION and WINDOW_2_DURATION columns. As an example, row two would have a WINDOW_1_DURATION of 60.

Ideally I'd like to achieve this without using functions or the creation of new tables.

Edit:

The desired output would look like this:

| USER| START_TIME       | END_TIME         | DURATION | WINDOW_1_START | WINDOW_1_END | WINDOW_2_START | WINDOW_2_END | WINDOW_1_DURATION | WINDOW_2_DURATION |
|-----|------------------|------------------|----------|----------------|--------------|-----------------|--------------|---------------------------------------|
| jim | 2021-01-24 14:00 | 2021-01-24 16:00 | 120      | 17:00          | 20:00        | 20:00           | 22:00        | 0                 | 0                 |
| jim | 2021-01-24 16:00 | 2021-01-24 18:00 | 120      | 17:00          | 20:00        | 20:00           | 22:00        | 60                | 0                 |
| jim | 2021-01-24 18:00 | 2021-01-24 19:30 | 90       | 17:00          | 20:00        | 20:00           | 22:00        | 90                | 0                 |
| jim | 2021-01-24 19:30 | 2021-01-24 22:00 | 150      | 17:00          | 20:00        | 20:00           | 22:00        | 30                | 120               |

Edit 2: As an explanation for my example of the WINDOW_1_DURATION being 60 for row 2, this is a measurement of the minutes between the START_TIME of 16:00 and the END_TIME of 18:00 that occur with the WINDOW_1_START of 17:00 and the WINDOW_1_END of 20:00.


Solution

  • Below query will deliver your desired output: (Logic: First I have CAST start_time and end_time as time in starttiem and endtime column. Then with Case I have tried to identify how overlapping happend. For example if WINDOW_1_START is less then starttime and WINDOW_1_END IS BETWEEN starttime and endtime then the overlapped time is between starttime and Window_1_End etc.)

    with cte as (
    SELECT [USER],
           START_TIME,
           END_TIME,
           DATEDIFF ( minute, START_TIME, END_TIME ) AS DURATION,
           CAST ( '17:00' AS TIME ) AS WINDOW_1_START,
           CAST ( '20:00' AS TIME ) AS WINDOW_1_END,
           CAST ( '20:00' AS TIME ) AS WINDOW_2_START,
           CAST ( '22:00' AS TIME ) AS WINDOW_2_END,
           cast(start_time as time)starttime,
           cast(end_time as time)endtime       
    FROM EVENTS)
    
    select * ,
    case when (WINDOW_1_START between starttime and endtime and WINDOW_1_end between starttime  and endtime  ) then (datediff(minute,WINDOW_1_START,WINDOW_1_end)) 
    when (WINDOW_1_START between starttime and endtime and WINDOW_1_END > endtime)  then (datediff(minute,WINDOW_1_START,endtime)) 
    when (WINDOW_1_START<starttime and WINDOW_1_END between starttime and endtime) then (datediff(minute,starttime,WINDOW_1_END))
    when (WINDOW_1_START<starttime and WINDOW_1_END >endtime) then (datediff(minute,starttime,endtime)) end WINDOW_1_DURATION,
    case when (WINDOW_2_START between starttime and endtime and WINDOW_2_end between starttime  and endtime  ) then (datediff(minute,WINDOW_2_START,WINDOW_2_end)) 
    when (WINDOW_2_START between starttime and endtime and WINDOW_2_END > endtime)  then (datediff(minute,WINDOW_2_START,endtime)) 
    when (WINDOW_2_START<starttime and WINDOW_2_END between starttime and endtime) then (datediff(minute,starttime,WINDOW_2_END))
    when (WINDOW_2_START<starttime and WINDOW_2_END >endtime) then (datediff(minute,starttime,endtime)) end WINDOW_2_DURATION
    
    from cte
    

    Output: enter image description here