正确的 SQL
我想按以下顺序获取 3 个消息组:[1,2]、[3,4]、[5]
要获得请求的订单,请添加ORDER BY min(id)
:
SELECT grp, user_id, array_agg(id) AS ids
FROM (
SELECT id
, user_id
, row_number() OVER (ORDER BY id) -
row_number() OVER (PARTITION BY user_id ORDER BY id) AS grp
FROM tbl
ORDER BY 1 -- for ordered arrays in result
) t
GROUP BY grp, user_id
ORDER BY min(id);
db<>fiddle here
旧的 sqliddle
增加几乎不能保证另一个答案。更重要的问题是:
使用 PL/pgSQL 更快
我正在使用 PostgreSQL,并且很乐意使用特定于它的东西,只要能提供最佳性能。
纯 SQL 一切都很好而且很闪亮,但是对于这个任务来说,程序化的服务器端函数要快得多。虽然以程序方式处理行通常较慢,但plpgsql赢得了这场竞赛,因为它可以使用单个表扫描和单个 ORDER BY
操作:
CREATE OR REPLACE FUNCTION f_msg_groups()
RETURNS TABLE (ids int[])
LANGUAGE plpgsql AS
$func$
DECLARE
_id int;
_uid int;
_id0 int; -- id of last row
_uid0 int; -- user_id of last row
BEGIN
FOR _id, _uid IN
SELECT id, user_id FROM messages ORDER BY id
LOOP
IF _uid <> _uid0 THEN
RETURN QUERY VALUES (ids); -- output row (never happens after 1 row)
ids := ARRAY[_id]; -- start new array
ELSE
ids := ids || _id; -- add to array
END IF;
_id0 := _id;
_uid0 := _uid; -- remember last row
END LOOP;
RETURN QUERY VALUES (ids); -- output last iteration
END
$func$;
称呼:
SELECT * FROM f_msg_groups();
基准和链接
我在具有 60k 行的类似现实生活表上进行了快速测试EXPLAIN ANALYZE
(执行多次,选择最快的结果以排除兑现效应):
SQL:
总运行时间:1009.549 毫秒
Pl/pgSQL:
总运行时间:336.971 毫秒
有关的: