How do I find gaps between ranges of numbers?
After a pretty good amount of digging, I found this blog post on doing this for date ranges, and it turns out to work equally well on numeric ranges, with a little modification for handling the 0 and 1 endpoints.

Taking the query from there and renaming the columns to be a bit more clear, we have

SELECT * FROM
  (SELECT MAX(T_END) OVER (ORDER BY T_START) GAP_START,
          LEAD(T_START) OVER (ORDER BY T_START) GAP_END
   FROM T)
WHERE GAP_START &lt; GAP_END


This warrants quite a bit of explanation. Step by step:


For each row, it gets the maximum T_END from all rows where T_START is less than or equal to the current row's T_START. This gives us the biggest range end of all the ranges that overlap this row's range start.
For each row, it gets the T_START of the first row where the T_START is greater than the current row. This gives us the next biggest range start.
It filters out any rows where the maximum T_END is greater than or equal to the next T_START. This means that the biggest overlapping range end extends out to or beyond the next range start. In other words, there is no gap between this row and the next, either because this row ends where the next begins or because other overlapping rows cover all points between the two.


This doesn't quite handle the requirement of all gaps over the range of 0 to 1, though. It only picks the gaps between the minimum range start and the maximum range end of all the rows. To check for gaps at the ends of the 0 and 1 range, we can use the following queries.

For gaps starting at 0:

SELECT 0 AS GAP_START, MIN(T_START) AS GAP_END
FROM T
HAVING MIN(T_START) &gt; 0


For gaps ending at 1:

SELECT MAX(T_END) AS GAP_START, 1 AS GAP_END
FROM T
HAVING MAX(T_END) &lt; 1


Both of these queries will return no rows if there is no gap at the endpoint.

Putting it all together into a single query:

SELECT * FROM
  (SELECT MAX(T_END) OVER (ORDER BY T_START) GAP_START,
          LEAD(T_START) OVER (ORDER BY T_START) GAP_END
   FROM T)
WHERE GAP_START &lt; GAP_END
UNION ALL
SELECT 0 AS GAP_START, MIN(T_START) AS GAP_END
FROM T
HAVING MIN(T_START) &gt; 0
UNION ALL
SELECT MAX(T_END) AS GAP_START, 1 AS GAP_END
FROM T
HAVING MAX(T_END) &lt; 1


The result is exactly as stated in the question, ignoring order. SQLFiddle of the query.