EXPLAINを読み解く

推測をやめて計画を見る

速度の話は推測が入り込みやすい領域です。「たぶんここが重い」で索引を足しても、外れていれば書き込みが重くなるだけです。

EXPLAIN は、データベースがこのクエリをどう処理するつもりかを教えてくれます。読み方さえ分かれば、推測が要らなくなります。

内側から外側へ読む

計画は入れ子になっています。深くくぼんだ行が先に実行され、その結果が外側へ渡ります。

Aggregate -> Bitmap Heap Scan on orders Recheck Cond: (customer_id = 777) -> Bitmap Index Scan on orders_customer_id_idx Index Cond: (customer_id = 777)

読む順番は下から上です。索引で該当箇所を見つけ、その行を取り出し、最後に集計している、と読めます。

走査の種類

最初に見るのが、テーブルをどう読むかです。

種類意味
Seq Scan全部の行を先頭から読む
Index Scan索引をたどって、必要な行だけ読む
Bitmap Index Scan索引で該当箇所の印を集めてから、まとめて読む
Index Only Scan索引の中だけで完結し、表を読まない

該当行が少ないときは Index Scan、そこそこ多いときは Bitmap が選ばれます。どちらも索引を使っているので、どちらでも構いません。

Index Cond と Filter を見分ける

走査の種類より重要なのが、条件がどこに書かれているかです。

表示意味
Index Cond索引の側で絞り込めている
Recheck Cond索引で絞ったあとの確認。問題無い
Filter行を全部読んでから捨てている

Filter が付いている行は、そこで大量に捨てているなら改善の余地があります

見積もり行数を疑う

各行の末尾には rows= という見積もりが出ます。データベースが統計情報から予測した件数です。

Bitmap Heap Scan on orders (cost=4.33..23.06 rows=5 width=20)

この見積もりが実際と大きく食い違っていると、選ばれた計画そのものが的外れになります。見積もり5件のつもりが実際は5万件、というときは ANALYZE を打って統計を作り直します。

実際に流して本当の件数を見たいときは EXPLAIN ANALYZE を使います。ただし本当に実行されるので、UPDATEDELETE に対して打つときは注意します。

この教材の環境について

ブラウザの中で動く PostgreSQL は、索引が使えるなら必ず索引を選ぶ設定になっています。そのため全表走査が選ばれたときの cost が極端に大きな値で表示されます。数字そのものではなく、どの走査が選ばれ、条件がどこに書かれたかを見てください。

手を動かす

3通りのクエリの計画を読んでから、実際の集計を出します。

テーブル構造

CREATE TABLE orders ( id integer PRIMARY KEY, store_id integer NOT NULL, customer_id integer NOT NULL, ordered_on date NOT NULL, status text NOT NULL, total integer NOT NULL ); INSERT INTO orders SELECT g, 1 + (g % 5), 1 + (g % 20000), DATE '2026-01-01' + (g % 366), (ARRAY['paid', 'shipped', 'cancelled'])[1 + (g % 3)], 1000 + (g % 50000) FROM generate_series(1, 100000) AS g; CREATE INDEX orders_customer_id_idx ON orders (customer_id); ANALYZE orders;

期待される出力

statuscnt
cancelled33333
paid33333
shipped33334

ヒント

query.sql
学習モード
コードの実行結果
データベースを初期化中...