基礎読めるようになる

実行計画を読む順番(木構造・内側から外側へ)

定義

実行計画は木構造で、インデントが深いノードほど先に実行される。矢印(->)が付いた行が子ノードで、親は子の結果を受け取ってから自分の処理をする。いちばん上の行が最後に実行される。

上から順に実行される、ではない

実行計画でいちばん多い誤読がこれです。実行計画は木構造で、いちばん上の行が最後に実行されます。

 Nested Loop  (cost=0.85..28.26 rows=4 width=17) (actual time=0.041..0.051 rows=4.00 loops=1)
   ->  Index Scan using members_pkey on members m  (cost=0.42..8.44 rows=1 width=17) (actual time=0.022..0.022 rows=1.00 loops=1)
         Index Cond: (id = 42)
         Index Searches: 1
   ->  Index Scan using orders_s_member_idx on orders_s o  (cost=0.43..19.78 rows=4 width=8) (actual time=0.017..0.024 rows=4.00 loops=1)
         Index Cond: (member_id = 42)
         Index Searches: 1
 Planning Time: 0.311 ms
 Execution Time: 0.099 ms

3 ノードの小さい計画。
PostgreSQL 18.6 / 2026-08-22 採取 / 並列・JIT は OFF

この計画は、上から読むとこう書いてあります。

  1. Nested Loop
  2. Index Scan using members_pkey on members m
  3. Index Scan using orders_s_member_idx on orders_s o
同じ計画を木にすると、動く順番が見える
Index Scanmembers m表示 21Index Scanorders_s o表示 32Nested Loop表示 13「表示 N」= テキストで上から N 行目のノード黒丸 = 実際に動く順番。いちばん深いところが 1 番、いちばん上が最後
テキストは木を縦に潰して並べたもの。 インデントが親子関係を表しているので、上から順に実行される、にはならない

実際に動く順番は 2 → 3 → 1 です。members から 1 行取り、その id を使ってorders_s を引き、その結果を Nested Loop が組み合わせます。

読み方は 3 つの規則だけ

1. 矢印(->)が付いている行がノード

矢印が付いていない行は、そのすぐ上のノードに対する補足です。Index Cond: Filter: Index Searches: Sort Method: Buffers: などがこれにあたります。 ノードではないので、読む順番を考えるときは無視して構いません。

Index Searches:PostgreSQL 18 で増えた行で、 そのノードがインデックスを何回探しに行ったかを表します。 繰り返し実行されるノードでは loops と同じ数になることが多く、「何回まわったか」を裏から確かめる材料になります。ただしこの行だけは 1 回あたりではなく累計です。だから loops と同じ数になります。 17 以前の出力を見慣れている人には見覚えのない行なので、先に触れておきます。

2. インデントが深い方が子

あるノードより右にずれている矢印が、そのノードの子です。 同じ深さに矢印が 2 つ並んでいれば、そのノードは子を 2 つ持っています (結合ノードがこれです)。

3. 子が先に動き、親は結果を受け取る

だからいちばん深いところから読み始めます。いちばん上の行は、全部終わったあとに最後の仕上げをするノードです。

子が 2 つあるとき

結合のノードは子を 2 つ持ちます。上が外側、下が内側です。

 Hash Join  (cost=4534.20..40595.24 rows=40310 width=17) (actual time=6.428..138.374 rows=40000.00 loops=1)
   Hash Cond: (o.member_id = m.id)
   ->  Seq Scan on orders_s o  (cost=0.00..30811.00 rows=2000000 width=8) (actual time=0.047..51.442 rows=2000000.00 loops=1)
   ->  Hash  (cost=4407.95..4407.95 rows=10100 width=17) (actual time=6.344..6.345 rows=10000.00 loops=1)
         Buckets: 16384  Batches: 1  Memory Usage: 636kB
         ->  Bitmap Heap Scan on members m  (cost=114.70..4407.95 rows=10100 width=17) (actual time=0.821..5.602 rows=10000.00 loops=1)
               Recheck Cond: (city = 'city-7'::text)
               Heap Blocks: exact=4167
               ->  Bitmap Index Scan on members_city_idx  (cost=0.00..112.17 rows=10100 width=0) (actual time=0.484..0.484 rows=10000.00 loops=1)
                     Index Cond: (city = 'city-7'::text)
                     Index Searches: 1
 Planning Time: 0.456 ms
 Execution Time: 139.601 ms

Hash Join。子が 2 つ並んでいる。
PostgreSQL 18.6 / 2026-08-22 採取 / 並列・JIT は OFF

子が 2 つあるとき — 枝分かれがそのまま結合
Seq Scanorders_s o表示 2Bitmap Index Scan表示 5Bitmap Heap Scanmembers m表示 4Hash表示 3Hash Join表示 1「表示 N」= テキストで上から N 行目のノード
同じ深さに矢印が 2 つ並ぶ = そのノードは子を 2 つ持つ。テキストでは上下に並ぶだけだが、木にすると左右の枝になる。 なお左右どちらの枝が先に動くかは、結合の種類で決まるHash Join は右の Hash を作り終えてから左を流す)。 そのためこの図では動く順番の番号を出していない。

この Hash Join は、Seq Scan on orders_s(外側)と Hash(内側)を子に持っています。Hash はさらに Bitmap Heap Scan on members を子に持っています。

動く順番は内側からです。先に members を読んでハッシュ表を作り、 そのあと orders_s を流しながら突き合わせます。 なぜその順番なのかは結合の種類に書いてあります。

いちばん上は「最後にやること」

木のいちばん上に来やすいノードには決まった顔ぶれがあります。

  • Limit — 必要な件数だけ取ったら止める
  • SortORDER BY のための並べ替え
  • Aggregate / GroupAggregate / HashAggregate — 集約

これらが上にあるということは、それ以外の全部が先に終わっているということです。

Limit は少し特別で、下のノードを途中で止められます。10 件そろった時点で「もういい」と言えるので、 下の actual rows が 10 で止まっていることがあります。

逆に、下のノードが 100 万行返しているのに Limit 10 が付いていたら、 止められなかったという意味です。あいだに Sort や集約が挟まっていると、 並べ替えるために全部読まないと 1 行目が決まらないためです。この場合、100 万行ぶんの仕事はもう終わっています。

ここまでで、あの計画のどこが読めるようになったか

セクションの最初のページに出した 2.16 秒の計画は、 全部で 25 行あります。その骨格はもう読めます。

  • 1 行目の Limit が最後に実行される(いちばん上 = 最後)
  • 19 行目のいちばん深いノードが最初に動く(いちばん深いところ = 最初)
  • 矢印が付いていない行は補足で、ノードではない

まだ読めないのは数字の意味だけです。costcost と rows の意味actual timeloopsEXPLAIN ANALYZE の見方へ。

読む順番が分かれば、時間の読み方も変わります。上の行に書いてある時間はその下で起きたこと全部を含んだ累積です。 「このノードが何 ms 使ったか」を知るには引き算が要ります。 そこはEXPLAIN ANALYZE の見方で扱います。

よくある疑問

Q.いちばん上の行が最初に実行されるのではないのですか?
A.逆です。いちばん上が最後に実行されます。上の行は下の行から結果を受け取って自分の処理をするので、下(インデントが深い方)が先です。
Q.子が 2 つあるとき、どちらが先ですか?
A.上に書かれている方が外側(Outer)、下が内側(Inner)です。Nested Loop なら外側を 1 行取るごとに内側を引き、Hash Join なら先に内側でハッシュ表を作ってから外側を流します。
Q.矢印がない行は何ですか?
A.そのノードに付く補足情報です。Index Cond や Filter、Sort Method などがこれにあたります。ノードそのものではないので、読む順番には関係しません。

関連トピック

もっと学びたい方へ(おすすめ書籍)

SQL実践入門 ── 高速でわかりやすいクエリの書き方
ミック

「なぜこの書き方が速いのか」を実行計画から説明する一冊。条件分岐・集約・結合・更新のそれぞれで、良い書き方と悪い書き方を対比しながら読める。

Amazon で見る →
おすすめ
達人に学ぶDB設計徹底指南書 第2版
ミック

テーブル設計と正規化、パフォーマンス考慮のインデックス設計まで実務レベルで学べる定番書。第2版ではクラウド対応も強化。

Amazon で見る →
おすすめ
楽々ERDレッスン (CodeZine BOOKS)
羽生章洋

ER 図をどう「使える設計」に落とすか、実務の判断まで踏み込んだ入門書。エンティティの切り出しから多対多の扱いまで具体例が豊富。

Amazon で見る →
おすすめ
スッキリわかるSQL入門 第4版 ドリル256問付き!
中山清喬/飯田理恵子

ドリル 256 問を実際に打ちながら進める SQL の入門書。付属のブラウザ環境で演習できるので、SELECT から結合・集約までを環境構築で止まらずに通せる。

Amazon で見る →
達人に学ぶSQL徹底指南書 第2版
ミック

SQLの本質的な使い方と、インデックスが効くクエリの書き方を学べる。ウィンドウ関数など現代SQLも網羅。

Amazon で見る →
SQLアンチパターン 第2版
Bill Karwin

実務でやりがちなSQL・DB設計のアンチパターンとその回避策を体系的に学べる。

Amazon で見る →
[改訂3版]内部構造から学ぶPostgreSQL
勝俣智成 ほか

PostgreSQLの内部構造・ストレージ・インデックス機構を丁寧に解説。設計と運用計画の鉄則が学べる。

Amazon で見る →
情報処理教科書 データベーススペシャリスト 2025年版
三好康之

IPAデータベーススペシャリスト試験の総合対策書。インデックス関連は本サイトと合わせて学ぶと理解が深まる。

Amazon で見る →
理論から学ぶデータベース実践入門
奥野幹也

リレーショナルモデルの理論から、インデックス設計を含む実務で使えるSQLまで解説。

Amazon で見る →

本セクションはAmazonアソシエイトのリンクを含みます。

オンライン個別指導

もっと深くDBを学びたい方へ。

たいてっくが、SQL・データベース設計・パフォーマンスチューニング・IPAデータベーススペシャリスト対策まで、1対1で学習をサポートします。まずは無料相談から。

無料相談を予約する →
受講者の声 (DB・SQL コース)
menta レビュー原文を見る →
  • 教え方も上手で、お人柄も良いメンターです。DB周りの知識はもちろん、何より、しっかり教えてあげようという姿勢がとてもありがたかったです。データベース、SQLの学習を考えている方にはおススメです。
    H 様DB・SQL コース受講
  • 体系的に知識を教えてくださり、実際の業務でも大変役立っております。特に短い時間で効率よく知識の習得や、練習をできているのは期待以上でした。
    M 様DB・SQL コース受講
  • 大変充実したコンテンツでわかりやすいご説明をありがとうございました。基本的な質問にも丁寧にご説明いただき、また業務のご相談にも乗って頂き大変有意義な時間でした。
    K 様DB・SQL コース受講