I have a slots
table like this :
Column | Type |
------------+-----------------------------+
id | integer
Gordon Linoff already provided the answer (I upvoted).
I've used the same approach, but wanted to deal with tsrange type. So I came up with this construct:
SELECT min(id) b_id, min(begin_at) b_at, max(end_at) e_at, grp, user_id
FROM (
SELECT t.*, sum(g) OVER (ORDER BY id) grp
FROM (
SELECT s.*, (NOT r -|- lag(r,1,r)
OVER (PARTITION BY user_id ORDER BY id))::int g
FROM (SELECT id,begin_at,end_at,user_id,
tsrange(begin_at,end_at,'[)') r FROM slots) s
) t
) u
GROUP BY grp, user_id
ORDER BY grp;
Unfortunately, on the top level one has to use min(begin_at)
and max(end_at)
, as there're no aggregate functions for the range-based union operator +
.
I create ranges with exclusive upper bounds, this allows me to use “is adjacent to” (-|-) operator. I compare current tsrange
with the one on the previous row, defaulting to the current one in case there's no previous. Then I negate the comparison and cast to integer
, which gives me 1
in cases when new group starts.