'Find gaps in SQL between datetime Ranges within a startdatetime and enddatetime value

im looking for a SQL Query that can deliver me all of the free time Intervals between a given Range for a Table with two datetime Columns (DATE_FROM, DATE_TILL). As a Requirement: All other entries are not overlapped and the only acceptable distance between each interval is 1 Second. I have found a Solution but this doesnt fill all my Requirements, specially the one where i want to put a given start and end datetime to calculate the missing Intervals if given.

Here is my datatable:

ROW_ID LOCATION_ID   DATE_FROM              DATE_TILL
1       193          2019-02-01 00:00:00    2019-12-31 23:59:59
2       193          2020-02-01 00:00:00    2020-12-31 23:59:59
3       193          2021-01-01 00:00:00    2021-12-31 23:59:59
4       193          2022-01-01 00:00:00    2022-12-31 23:59:59
5       204          2020-01-01 00:00:00    2021-12-31 23:59:59

And this is my SQL Query, which is from another Solution in this Plattform where i made some requirements changes.

SELECT DATE_ADD(DATE_TILL,INTERVAL 1 SECOND) AS GAP_FROM, DATE_SUB(DATE_FROM,INTERVAL 1 SECOND) AS GAP_TILL
FROM
    (
        SELECT DISTINCT DATE_FROM, ROW_NUMBER() OVER (ORDER BY DATE_FROM) RN
        FROM overlappingtable T1
        WHERE
            LOCATION_ID = 193 AND
            NOT EXISTS (
                SELECT *
                FROM overlappingtable T2
                WHERE T1.DATE_FROM > T2.DATE_FROM AND T1.DATE_FROM < T2.DATE_TILL
            )
        ) T1
    JOIN (
        SELECT DISTINCT DATE_TILL, ROW_NUMBER() OVER (ORDER BY DATE_TILL) RN
        FROM overlappingtable T1
        WHERE
                LOCATION_ID = 193 AND
            NOT EXISTS (
                SELECT *
                FROM overlappingtable T2
                WHERE T1.DATE_TILL > T2.DATE_FROM AND T1.DATE_TILL < T2.DATE_TILL
            )
    ) T2
    ON T1.RN - 1 = T2.RN
WHERE
   DATE_ADD(DATE_TILL,INTERVAL 1 SECOND) < DATE_FROM

This Query delivers me this result:

GAP_FROM              GAP_TILL
2020-01-01 00:00:00   2020-01-31 23:59:59

Which is great, this is the free Interval that i have to deliver between entries that have their ranges and dont overlap. But I want to set in this Query two Parameters for The Main Range for this entries. One for the startdate and the other for enddate. For this example:

startdate = '2019-01-01 00:00:00'
enddate = '9999-12-31 23:59:59'

For LOCATION_ID = 193 i am missing the gap between the startdate('2019-01-01 00:00:00') and the first DATE_FROM for the first entry('2019-02-01 00:00:00').

The result that i would like to deliver should look like this for LOCATION_ID = 193:

GAP_FROM              GAP_TILL
2019-01-01 00:00:00   2019-01-31 23:59:59
2020-01-01 00:00:00   2020-01-31 23:59:59
2023-01-01 00:00:00   9999-12-31 23:59:59

Im really new at SQL and could understand this Query, but i can't develop this further to set these Main Ranges and deliver the missing gaps.

Thanks in Advance



Solution 1:[1]

You could join to a sub-query with the start & end datetimes.

Then compare to the previous & next datetimes per location_id.

The previous or next datetimes can be found via the LAG & LEAD functions.

WITH CTE_UNDERLAPS AS 
(
  SELECT t.*
  , LAG(DATE_TILL)  OVER (PARTITION BY LOCATION_ID ORDER BY DATE_FROM, DATE_TILL) AS PREV_DATE_TILL
  , LEAD(DATE_FROM) OVER (PARTITION BY LOCATION_ID ORDER BY DATE_FROM, DATE_TILL) AS NEXT_DATE_FROM
  , l.*
  FROM overlappingtable t
  JOIN (
    SELECT 
      CAST('2019-01-01 00:00:00' AS DATETIME) AS START_DATETIME
    , CAST('9999-12-31 23:59:59' AS DATETIME) AS END_DATETIME
  ) l ON DATE_FROM >= START_DATETIME AND DATE_TILL <= END_DATETIME
) 
SELECT LOCATION_ID
, COALESCE(DATE_ADD(PREV_DATE_TILL,INTERVAL 1 SECOND), START_DATETIME) AS DATE_FROM
, DATE_SUB(DATE_FROM,INTERVAL 1 SECOND) AS DATE_TILL
FROM CTE_UNDERLAPS
WHERE COALESCE(DATE_ADD(PREV_DATE_TILL,INTERVAL 1 SECOND), START_DATETIME) < DATE_FROM
UNION
SELECT LOCATION_ID
, DATE_ADD(DATE_TILL,INTERVAL 1 SECOND) AS DATE_FROM
, COALESCE(DATE_SUB(NEXT_DATE_FROM,INTERVAL 1 SECOND), END_DATETIME) AS DATE_TILL
FROM CTE_UNDERLAPS
WHERE DATE_ADD(DATE_TILL,INTERVAL 1 SECOND) < COALESCE(NEXT_DATE_FROM, END_DATETIME)

ORDER BY LOCATION_ID, DATE_FROM, DATE_TILL
LOCATION_ID DATE_FROM DATE_TILL
193 2019-01-01 00:00:00 2019-01-31 23:59:59
193 2020-01-01 00:00:00 2020-01-31 23:59:59
193 2023-01-01 00:00:00 9999-12-31 23:59:59
204 2019-01-01 00:00:00 2019-12-31 23:59:59
204 2022-01-01 00:00:00 9999-12-31 23:59:59

Demo on db<>fiddle here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 LukStorms