Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PostgreSQL query to detect overlapping time ranges

Tags:

sql

postgresql

I have a table in PostgreSQL 9.2 that looks like this (simplified):

CREATE TABLE my_features
(
  id integer NOT NULL,
  feature_id integer NOT NULL,
  begin_time timestamp NOT NULL,
  end_time timestamp
)

For each feature_id there may be multiple rows with time ranges specified by begin_time/end_time. They may overlap, but this is relatively rare. I'm looking for a fast way to find all feature_ids that have/don't have any overlaps.

I tried to do this using window functions, like this:

SELECT feature_id, bool_or(end_time > lead(begin_time) OVER ts_win) OVER ts_win AS overlaps_any
FROM my_features
WINDOW ts_win AS (PARTITION BY feature_id ORDER BY begin_time)

... but this doesn't work:

ERROR:  window function calls cannot be nested

The algorithm is simple: order the rows for a given feature_id by begin_time and check if any end_time > the next begin_time (if any). I suspect there must be an easy way to do this, perhaps with tsrange functions, but can't seem to find it just now.

like image 388
EM0 Avatar asked Sep 08 '14 21:09

EM0


People also ask

How do you calculate range overlap?

Calculate total cost based on different rates per hourCount overlapping hours for the first range 00:00-08:00 and multiply with rate 8. returns 16. Count overlapping hours for the second range 08:00-18:00 and multiply with rate 5. Count overlapping hours for the second range 18:00-24:00 and multiply with rate 10.

What is Postgres overlap?

In PostgreSQL, you can use the OVERLAPS operator to test for overlapping time periods. The function returns true when two time periods (defined by their endpoints) overlap, and false when they do not overlap.

How do I select a timestamp in PostgreSQL?

select 'date: time' :: timestamp; select CURRENT_TIMESTAMP :: timestamp; Below is the parameter description of the above syntax are as follows: Select: Select is used to select timestamp value in timestamp syntax.


1 Answers

This can indeed be done using range types.

The following selects all those rows that do have overlapping ranges:

select f1.*
from my_features f1
where exists (select 1
              from my_features f2
              where tsrange(f2.begin_time, f2.end_time, '[]') && tsrange(f1.begin_time, f1.end_time, '[]')
                and f2.feature_id = f1.feature_id
                and f2.id <> f1.id);

When you change the condition to NOT EXISTS you'll find those that don't have any overlapping ranges.

SQLFiddle example: http://sqlfiddle.com/#!15/40b1e/1

tsrange(f2.begin_time, f2.end_time, '[]') creates a range that includes the upper and lower bounds. You can also create ranges that exclude either one or both.

More details can be found in the manual:
http://www.postgresql.org/docs/current/static/rangetypes.html#RANGETYPES-INCLUSIVITY

The && operator checks if the two ranges overlap: http://www.postgresql.org/docs/current/static/functions-range.html

(I just wish Oracle had something fancy like that...)

like image 170
a_horse_with_no_name Avatar answered Sep 20 '22 16:09

a_horse_with_no_name