KDOC 658: 集計や並べ替えの後ろにある LIMIT は読む行を減らさないのを確かめる

この文書のステータス

  • 作成
    • 2026-10-13 貴島
  • レビュー
    • 2026-10-13 貴島

概要

集計や並べ替えの後ろにある LIMIT は、下の処理が全部の行を読み終えるまで効かないので、読む行を減らさないのを確かめる。

ユーザー、注文、明細を結合し、ユーザーごとに金額を合計して、上位5件を取る。比べるため、集計と並べ替えを外して 5 件取る問い合わせも流す。

SET max_parallel_workers_per_gather = 0;
CREATE TABLE users (id int PRIMARY KEY, name text);
CREATE TABLE orders (id int PRIMARY KEY, user_id int);
CREATE TABLE items (order_id int, price int);
INSERT INTO users
SELECT i, 'user' || i FROM generate_series(1, 10000) i;
INSERT INTO orders
SELECT i, i % 10000 + 1 FROM generate_series(1, 100000) i;
INSERT INTO items
SELECT i % 100000 + 1, i % 1000 FROM generate_series(1, 300000) i;
ANALYZE users;
ANALYZE orders;
ANALYZE items;

-- 集計して並べ替えてから 5 件取る
EXPLAIN (ANALYZE, FORMAT JSON, COSTS OFF, TIMING OFF,
         SUMMARY OFF, BUFFERS OFF)
SELECT u.name, sum(i.price) AS total
FROM users u
JOIN orders o ON o.user_id = u.id
JOIN items i ON i.order_id = o.id
WHERE u.id < 100
GROUP BY u.name
ORDER BY total DESC
LIMIT 5;

-- 集計も並べ替えもせずに 5 件取る
EXPLAIN (ANALYZE, FORMAT JSON, COSTS OFF, TIMING OFF,
         SUMMARY OFF, BUFFERS OFF)
SELECT u.name, i.price
FROM users u
JOIN orders o ON o.user_id = u.id
JOIN items i ON i.order_id = o.id
WHERE u.id < 100
LIMIT 5;

plan-2afc5d2495db.svg plan-3de4fc1f58c4.svg

  • 集計して並べ替えると、下の items の Seq Scan は 300,000 行をすべて読み、最後の Limit で 5 行に絞っている
  • 集計も並べ替えもしないと、items の Seq Scan は 5 行しか読まない。Limit が 5 行を受け取った時点で、下に行を要求するのをやめている。結合のしかたも Hash Join から、1 行ずつ相手を探す Nested Loop に変わった

計画は上のノードが下のノードに 1 行ずつ要求し、行は下のノードで作られて上へ渡る。上が要求をやめれば、下も止まる。

これは本質的に要求引き寄せ型(demand-pull)パイプライン機能です。 計画ノードが呼ばれる度にもう1つの行を引き渡すか、行を引き渡したことの報告を行わなければなりません。

51.6. エグゼキュータ

関連