I have a table with datetime fields start
and end
. And I have a list of (start, end) items. I need to check which items from the list overlap with data in the table.
The current query looks like this:
select br.duration from booking, (
select tstzrange('2016-09-06 03:45:00+00', '2016-09-06 14:45:00+00') as duration
union select tstzrange('2016-09-06 14:45:00+00', '2016-09-06 15:45:00+00') as duration
-- other items from my list
) as br
where tstzrange(start, end) && br.duration
Are there any other ways to do it? Do you think it will work if I have millions rows in the table and will compare them with hundreds items from the list?
Best Answer
I suggest a couple of important improvements for dealing with a million rows:
While providing your list of values with the needlessly verbose and expensive form
SELECT ... UNION ...
, make thatUNION ALL
, or Postgres will waste time trying to fold duplicates. And you would only need to declare column name(s) and data type(s) for the firstSELECT
of theUNION
query.But a
VALUES
expression is simpler and faster. Or provide an arraytstzrange[]
and useunnest()
:The query you have would return one row for every overlapping row in
booking
, while you probably want each overlapping value from the list once, most likely. You could addDISTINCT
orGROUP BY
to get unique rows, but that would still be a waste of time. AnEXISTS
semi-join is one of the much simpler and cheaper alternatives for your case: Each row fromduration
is returned exactly once if an overlapping entry is found and Postgres can stop looking further for this row.The query would still be slow without index support. Create a functional GiST or SP-GiST index. The latter probably performing best:
Related: