適切な 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
古いsqliddle
追加は、別の答えをほとんど保証しません。より重要な問題は次のとおりです。
PL/pgSQL で高速化
私は PostgreSQL を使用していますが、最高のパフォーマンスが得られるものであれば、PostgreSQL に固有のものを喜んで使用します。
純粋な SQL はどれも優れた機能を備えていますが、このタスクでは手続き型のサーバー側関数の方がはるかに高速です。行を手続き的に処理するのは一般的に遅くなりますが、plpgsqlは1 回のテーブル スキャンと 1回 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();
ベンチマークとリンク
EXPLAIN ANALYZE
60k行の同様の実際のテーブルで簡単なテストを実行しました(数回実行し、キャッシュ効果を除外するために最速の結果を選択します):
SQL:
合計実行時間: 1009.549 ミリ秒
Pl/pgSQL:
合計実行時間: 336.971 ミリ秒
関連している: