我有一个 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!
谢谢!
这是解决此问题的一种方法。创建一个标志,确定是否有一条记录 不 与前一个重叠。这是一个小组的开始。然后获取此标志的累积总和并将其用于分组:
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;
这里 是一个SQL小提琴。
Gordon Linoff已经提供了答案(我赞成)。
我使用了相同的方法,但想要处理 tsrange
类型。
所以我想出来了 这个结构:
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)
,因为基于范围的联合运营商没有集合函数 +
。
我创建了具有独占上限的范围,这允许我使用 “毗邻”(-|-
)运营商。我比较当前 tsrange
与上一行中的那一行相同,如果之前没有,则默认为当前行。然后我否定了比较并投了 integer
,这给了我 1
在新组开始的情况下。