Merge continuous rows with Postgresql

前端 未结 2 1473
没有蜡笔的小新
没有蜡笔的小新 2021-02-13 18:35

I have a slots table like this :

   Column   |            Type             |
------------+-----------------------------+
 id         | integer               


        
相关标签:
2条回答
  • 2021-02-13 19:15

    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.

    0 讨论(0)
  • 2021-02-13 19:17

    Here is one method for solving this problem. Create a flag that determines if a one record does not overlap with the previous one. This is the start of a group. Then take the cumulative sum of this flag and use that for grouping:

    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 is a SQL Fiddle.

    0 讨论(0)
提交回复
热议问题