问题 使用Postgresql合并连续行


我有一个 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!

谢谢!


5629
2017-10-12 20:13


起源

你可以找到你的数据吗? - Mihai
与SQL Server类似的问题 stackoverflow.com/questions/7520556/... - Ram
使用递归查询,从前端无法连接的begin-stubs开始。通过加入以下细分来扩展。最终只选择最长的路径。 - wildplasser


答案:


这是解决此问题的一种方法。创建一个标志,确定是否有一条记录  与前一个重叠。这是一个小组的开始。然后获取此标志的累积总和并将其用于分组:

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小提琴。


12
2017-10-12 21:09





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 在新组开始的情况下。


2
2017-10-12 21:40