FIG-091

実行計画(EXPLAIN) — 索引が速いとは限らない

データベース 2026.09.07 公開 読了 約12分

SQLは「何が欲しいか」しか書きません。どの索引を使うか、どちらの条件から絞るかは一切書いていない。それを決めているのがオプティマイザで、決めた道順が実行計画です。

EXPLAIN は、この道順を実行前に見せてもらうコマンドです。そして重要なのは、最適な道順はクエリの形だけでは決まらないということ。データの中身しだいで、正解が入れ替わります。図1で条件の当たり具合を動かして、選ばれる経路がひっくり返る瞬間を見てください。

EXPLAIN — 同じクエリでも、速い道順は中身で変わる
SELECT * FROM orders WHERE status = 'shipped' AND created_at > '2026-01-01'
Seq Scan
先頭から全部めくる
読む行数 —
—
Index Scan (status)
statusの索引から引く
読む行数 —
—
Index Scan (created_at)
日付の索引から引く
読む行数 —
—
orders テーブル(10万行) — 実際に触った場所
読んだ 条件に合った 触っていない
PLANNING
まだ1行も読んでいない。見積もりだけで選ぶ
オプティマイザは実際にデータを読む前に、それぞれの道順で何行触ることになるかを見積もります。いちばん安い1つが選ばれます。「実行する」で、選ばれた道順を走らせてください。
図1 — 索引が速いとは限らない。当たる行が多いほど、全部めくったほうが速くなる

同じクエリに、いくつもの道順がある

status と created_at の2つの条件があるとき、取り方は少なくとも3つあります。先頭から全部めくって条件に合うものを拾う(Seq Scan)、statusの索引から引いて残りを絞る、日付の索引から引いて残りを絞る。

結果はどれも同じです。違うのは途中で何行触るかだけ。オプティマイザは統計情報(その値がだいたい何%を占めるか、といった情報)をもとにそれぞれの手間を見積もり、いちばん安いものを選びます。EXPLAINが見せているのは、この選ばれた道順と、見積もりの数字です。

索引が速いとは限らない

ここが実行計画のいちばん面白いところです。図1のスライダーを右へ動かして該当する行を増やしていくと、どこかでSeq Scanが勝ちます。

理由は、索引を使った取り出しが「索引を引く → 本体をその1行だけ取りに行く」を該当行のぶんだけ繰り返すからです。1件あたりの単価が高い。該当が全体の数%なら圧勝ですが、3割にもなると、順番に全部読むほうが安くなります。順番に読むのは1件あたりが極端に安いからです。

「索引を張ったのに使われない」という現象の多くは、故障ではなくオプティマイザが「そっちのほうが遅い」と判断した結果です。

見積もりが外れると、道順も外れる

この判断はすべて統計情報という「だいたいの分布」に依存しています。だから統計が古いと、判断も古くなります。大量にデータを入れた直後などに急に遅くなるのは、実態と見積もりがズレて間違った道順を選んでいるのが典型的な原因です。

見積もりと実際を突き合わせるのが EXPLAIN ANALYZE です。推定行数と実測行数が桁違いにズレていないか――遅いクエリを調べるとき、最初に見るのはここです。ズレていれば統計を取り直す、ズレていなければ索引や書き方を疑う、と切り分けられます。

用語ミニ辞書
実行計画
DBが選んだデータの取り方の手順。EXPLAINで見られる。
オプティマイザ
候補の中から、見積もりコストが最小の道順を選ぶ仕組み。
Seq Scan
先頭から順に全部読む方法。1行あたりが安く、該当が多いと有利。
Index Scan
索引から該当行を引く方法。該当が少ないときに圧倒的に速い。
統計情報
値の分布などの概算データ。これが古いと見積もりが外れる。

まとめ

実行計画は「同じ答えにたどり着く複数の道順から、安そうな1本を選んだ記録」です。選択はクエリの形ではなくデータの中身で決まるので、索引が常に正解とは限りません。図1でスライダーを左右に振ると、正解が入れ替わる瞬間がそのまま見えます。遅いクエリに出会ったら、まず EXPLAIN でどの道順を選んだのかを確かめるところから始めます。