Postgres SQL GROUP BY没有跳行?

Mos*_*hev 3 sql postgresql group-by

假设我在表中有这些数据:

 id | thing | operation | timestamp
----+-------+-----------+-----------
  0 | foo   |       add |         0
  0 | bar   |       add |         1
  1 | baz   |    remove |         2
  1 | dim   |       add |         3
  0 | foo   |    remove |         4
  0 | dim   |       add |         5
Run Code Online (Sandbox Code Playgroud)

是否有任何方法可以构建一个Postgres SQL查询,该查询将按ID和操作进行分组,但是没有将具有更高时间戳值的行分组给那些具有更低时间戳的行?我想从查询中得到这个:

 id |  things  | operation
----+----------+-----------
  0 | foo, bar |       add
  1 |      baz |    remove
  1 |      dim |       add
  0 |      foo |    remove
  0 |      dim |       add
Run Code Online (Sandbox Code Playgroud)

基本上分组,但仅限于按时间戳排序的相邻行.

Gar*_*thD 7

这是一个空白和孤岛问题(尽管本文针对的是SQL-Server,它描述的问题很好,所以仍然适用于Postgresql),并且可以使用排名函数来解决:

SELECT  id,
        thing,
        operation,
        timestamp,
        ROW_NUMBER() OVER(ORDER BY timestamp) - 
                ROW_NUMBER() OVER(PARTITION BY id, operation ORDER BY Timestamp) AS groupingSet,
        ROW_NUMBER() OVER(ORDER BY timestamp) AS PositionInSet,
        ROW_NUMBER() OVER(PARTITION BY id, operation ORDER BY Timestamp) AS PositionInGroup
FROM    T
ORDER BY timestamp;
Run Code Online (Sandbox Code Playgroud)

正如您所看到的那样,通过获取集合中的整体位置,并扣除组中的位置,您可以识别岛屿,其中每个独特的组合(id, operation, groupingset)代表岛屿:

id  thing   operation   timestamp   groupingSet PositionInSet   PositionInGroup
0   foo     add         0           0           1               1
0   bar     add         1           0           2               2           
1   baz     remove      2           2           3               1
1   dim     add         3           3           4               1
0   foo     remove      4           4           5               1
0   dim     add         5           3           6               3
Run Code Online (Sandbox Code Playgroud)

然后你只需要将它放在子查询中,并按相关字段分组,并使用string_agg连接你的东西:

SELECT  id, STRING_AGG(thing) AS things, operation
FROM    (   SELECT  id,
                    thing,
                    operation,
                    timestamp,
                    ROW_NUMBER() OVER(ORDER BY timestamp) - 
                            ROW_NUMBER() OVER(PARTITION BY id, operation ORDER BY Timestamp) AS groupingSet
            FROM    T
        ) AS t
GROUP BY id, operation, groupingset;
Run Code Online (Sandbox Code Playgroud)