我有一个slots
表,如下所示:
Column | Type |
------------+-----------------------------+
id | integer |
begin_at | timestamp without time zone |
end_at | timestamp without time zone |
user_id | integer |
我想连续选择合并的行。假设我有(简化)数据,如:
(1, 5:15, 5:30, 1)
(2, 5:15, 5:30, 2)
(3, 5:30, 5:45, 2)
(4, 5:45, 6:00, 2)
(5, 8:15, 8:30, 2)
(6, 8:30, 8:45, 2)
我想知道是否可以选择格式为:
的行(5:15, 5:30, 1)
(5:15, 6:00, 2) // <======= rows id 2,3 and 4 merged
(8:15, 8:45, 2) // <======= rows id 5 and 6 merged
修改 这是SQLfiddle
我正在使用Postgresql,版本9.3!
谢谢!
答案 0 :(得分:12)
这是解决此问题的一种方法。创建一个标志,确定一条记录是否不与前一条记录重叠。这是一个小组的开始。然后获取此标志的累积总和并将其用于分组:
select user_id, min(begin_at) as begin_at, max(end_at) as end_at
from (select s.*, sum(startflag) over (partition by user_id order by begin_at) as grp
from (select s.*,
(case when lag(end_at) over (partition by user_id order by begin_at) >= begin_at
then 0 else 1
end) as startflag
from slots s
) s
) s
group by user_id, grp;
Here是一个SQL小提琴。
答案 1 :(得分:2)
Gordon Linoff已经提供了答案(我投了赞成票)。
我使用了相同的方法,但想要处理tsrange
type。
所以我想出了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;
不幸的是,在顶层,必须使用min(begin_at)
和max(end_at)
,因为基于范围的联合运算符+
没有聚合函数。
我创建了具有独占上限的范围,这允许我使用“is adjacent to” (-|-
) operator。我将当前tsrange
与前一行中的integer
进行比较,默认为当前行,以防前一行没有。然后我否定比较并转换为1
,在新小组开始的情况下,这会给我{{1}}。