There are multiple ways of doing this, but the basic idea would be something like:
WITH mytable AS
(
SELECT row_number() OVER () rid, id, starttime, endtime
FROM myrealtable
)
SELECT distinct id
FROM mytable mt1
WHERE exists
(SELECT *
FROM mytable mt2
WHERE mt2.id = mt1.id
AND mt2.starttime >= mt1.starttime and mt2.starttime < mt1.endtime)
OR exists
(SELECT *
FROM mytable mt2
WHERE mt2.id = mt1.id
AND mt2.endtime > mt1.starttime and mt2.endtime <= mt1.endtime)
Or:
WITH mytable AS
(
SELECT row_number() OVER () rid, id, starttime, endtime
FROM myrealtable
)
SELECT distinct mt1.id
FROM mytable mt1 INNER JOIN mytable mt2
ON mt2.id = mt1.id and mt2.rid <> mt1.rid
WHERE (mt2.starttime >= mt1.starttime and mt2.starttime < mt1.endtime)
OR (mt2.endtime > mt1.starttime and mt2.endtime <= mt1.endtime)
The CTE is just to get a unique id (rid
) from row_number
to ensure we don't match the row with itself. You might have an alternate way of doing this (e.g. maybe you know all the columns can't be the same).
If you want to handle the times inclusively, you could use BETWEEN
to make the conditions a little simpler, but I assume 10:00-11:00 and 11:00-11:30 should not be considered overlaps.
I listed two different ways since I think the first method (with EXISTS
) is very human-readable and easy to understand what it's doing. The second method (with a JOIN
) is probably better (shorter and might end up optimized better) and should make sense if you understand the first example.
Here's a DB fiddle showing it.