The Query:
Assuming the fields containing the interval are named Start
and Finish
, and the table is named YOUR_TABLE
, the query...
SELECT Finish, Start
FROM
(
SELECT DISTINCT Start, ROW_NUMBER() OVER (ORDER BY Start) RN
FROM YOUR_TABLE T1
WHERE
NOT EXISTS (
SELECT *
FROM YOUR_TABLE T2
WHERE T1.Start > T2.Start AND T1.Start < T2.Finish
)
) T1
JOIN (
SELECT DISTINCT Finish, ROW_NUMBER() OVER (ORDER BY Finish) RN
FROM YOUR_TABLE T1
WHERE
NOT EXISTS (
SELECT *
FROM YOUR_TABLE T2
WHERE T1.Finish > T2.Start AND T1.Finish < T2.Finish
)
) T2
ON T1.RN - 1 = T2.RN
WHERE
Finish < Start
...gives the following result on your test data:
Finish Start
2012-01-01 10:00:00.000 2012-01-01 18:00:00.000
The important property of this query is that it would work on overlapping intervals as well.
The Algorithm:
1. Merge Overlapping Intervals
The subquery T1
accepts only those interval starts that are outside other intervals. The subquery T2
does the same for interval ends. This is what removes overlaps.
The DISTINCT
is important in case there are two identical interval starts (or ends) that are both outside other intervals. The WHERE Finish < Start
simply eliminates any empty intervals (i.e. duration 0).
We also attach a row number relative to temporal ordering, which will be needed in the next step.
The T1
yields:
Start RN
2012-01-01 08:00:00.000 1
2012-01-01 18:00:00.000 2
The T2
yields:
Finish RN
2012-01-01 10:00:00.000 1
2012-01-01 20:00:00.000 2
2. Reconstruct the Result
We can now reconstruct either the "active" or the "inactive" intervals.
The inactive intervals are reconstructed by putting together end of the previous interval with the beginning of the next one, hence - 1
in the ON
clause. Effectively, we put...
Finish RN
2012-01-01 10:00:00.000 1
...and...
Start RN
2012-01-01 18:00:00.000 2
...together, resulting in:
Finish Start
2012-01-01 10:00:00.000 2012-01-01 18:00:00.000
(The active intervals could be reconstructed by putting rows from T1
alongside rows from T2
, by using JOIN ... ON T1.RN = T2.RN
and reverting WHERE
.)
The Example:
Here is a slightly more realistic example. The following test data:
Device Event Start Finish
Device 1 Event A 2012-01-01 08:00:00.000 2012-01-01 10:00:00.000
Device 2 Event B 2012-01-01 18:00:00.000 2012-01-01 20:00:00.000
Device 3 Event C 2012-01-02 11:00:00.000 2012-01-02 15:00:00.000
Device 4 Event D 2012-01-02 10:00:00.000 2012-01-02 12:00:00.000
Device 5 Event E 2012-01-02 10:00:00.000 2012-01-02 15:00:00.000
Device 6 Event F 2012-01-03 09:00:00.000 2012-01-03 10:00:00.000
Gives the following result:
Finish Start
2012-01-01 10:00:00.000 2012-01-01 18:00:00.000
2012-01-01 20:00:00.000 2012-01-02 10:00:00.000
2012-01-02 15:00:00.000 2012-01-03 09:00:00.000