そのクエリ、本当に速い?実行計画で読み解くSQLの真実

SQLが「動く」ことと「速く動く」ことはまったくの別物。
アプリケーションがレスポンス遅延を起こしていて、SQLを見直したら意外なところにボトルネックがあった――そんな経験、あなたにもあるのでは?

SQLの速度を“感覚”ではなく“証拠”で語るための武器が、実行計画(Execution Plan)です。

この記事では、SQLのパフォーマンスを科学的に診断するために不可欠な「実行計画」の読み解き方と、そこから見えてくるボトルネックの正体をエンジニア目線で徹底解説します。


実行計画とは何か?

実行計画は、RDBMS(MySQLやPostgreSQLなど)がSQLクエリをどの順番で、どのように処理するかを示した設計図です。
DBはSQL文を読み取り、それを内部的な手続きに変換し、最も効率が良いと思われる「実行戦略」を自動で選びます。

しかしこの“選び方”が常にベストとは限りません。時には誤った統計情報や複雑なJOIN構造のせいで非効率な実行ルートが選ばれることもあるのです。


実行計画の出力方法

MySQLの場合:

EXPLAIN SELECT * FROM users WHERE age > 30;

PostgreSQLの場合:

EXPLAIN ANALYZE SELECT * FROM users WHERE age > 30;
  • EXPLAINだけだと「どう処理されるか」が分かります。
  • EXPLAIN ANALYZEを使うと「実際にどれくらい時間がかかったか」「行数は何件だったか」といったリアルな実行結果が付加されます。

読み解くべき5つのポイント

1. スキャンの種類を確認せよ

  • Full Table Scan(Seq Scan):テーブル全体をなめて読む。最も遅いパターン。
  • Index Scan / Index Seek:インデックスを利用した高速アクセス。

例(PostgreSQL):

Seq Scan on users  (cost=0.00..34.50 rows=1000 width=50)

→ これは全件スキャン。WHERE句のカラムにインデックスを貼る余地あり。


2. 行数の予測 vs 実際

PostgreSQLのEXPLAIN ANALYZEでは、予測(rows)と実測(actual rows)が出力されます。
これが大きく乖離していたら、統計情報の更新不足や条件の選択性が見誤られている
可能性大。


3. コストの合計値をチェック

MySQLではrowsfilteredを、PostgreSQLではcost=START..ENDの値を確認。

cost=0.00..1293.00

この1293.00がクエリの総合コスト。複数のJOINやネストがある場合は、最も重いノードがどこかを見極めるヒントになります。


4. JOINの順番とアルゴリズム

JOINが複数ある場合、それが**Nested Loop(ネストループ)**なのか、Hash Joinなのかも確認しましょう。

  • Nested Loop:少量データには有効。だが大規模データでは地獄。
  • Hash Join:一時的にハッシュテーブルを作って結合する。中規模向き。
  • Merge Join:ソート済みのテーブルをマージする方式。効率が良いが前提条件あり。

5. フィルタ条件の適用場所

Filter:という表示で、どの条件がどこで適用されているかがわかります。
テーブルの読み出し後にフィルターされている場合は、インデックスが効いていない証拠です。


実行計画から見えてくる改善ポイント

以下のような改善が実行計画を通じて導き出せます。

  • インデックスの新設・見直し
  • 統計情報の更新(ANALYZEVACUUM
  • クエリの書き換え(サブクエリ → JOIN など)
  • 不要なカラムの削除、絞り込みの追加
  • 結合順序の最適化、ヒント句(MySQLのSTRAIGHT_JOINなど)の活用

最後に:実行計画は“クエリのカルテ”

SQLチューニングの世界には魔法はありません。
あるのは、「読み解く力」と「小さな修正の積み重ね」です。

実行計画は、あなたのクエリがどこで時間を使い、どこでつまずいているかを教えてくれる“カルテ”のようなもの。
「なんか遅いな」ではなく、「ここが遅い」と言えることが、エンジニアとしての第一歩。

ぜひ日頃からEXPLAINと仲良くなって、“意図して速くするSQL”を書いていきましょう。


システム開発なんでもパートナー
システム開発なんでもパートナー

この記事を書いた人