Seq ScanとIndex ScanとIndex-Only Scan
プランナーがそれぞれを選ぶ理由と、クエリについて何が分かるかを学びます。
「Seq ScanとIndex ScanとIndex-Only Scan」はCoddyKit上の無料SQL Interview Prepレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSQL Interview Prep学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 SQL Interview Prepコースには全4レッスンが含まれています。
テーブルを読み取る3つの方法
プランナーがテーブルから行を取得する必要があるときは、3つのアクセス方式のいずれかを選択します。面接官は、その3つをすべて挙げることを期待しています。
- Seq Scan。テーブルのすべての行を最初から最後まで読み取ります。
- Index Scan。インデックスをたどって一致する行を見つけ、その後、各行をテーブルから取得します。
- Index-Only Scan。テーブルには一切アクセスせず、インデックスだけで処理を完結します。
プランナーがなぜそれぞれを選ぶのかを知ることが、このレッスンの核心であり、シニア向け面接で必ず問われる内容です。
Sequential Scanの動作
Seq Scanはテーブルのページを順番に読み取り、各行にフィルターを適用します。インデックスは参照しません。
悪い方法のように聞こえますが、これが正しい選択であることもよくあります。シーケンシャル読み取りはディスク上で高速です(ランダムなジャンプがないためです)。そのため、クエリがテーブルの大部分を返す場合は、インデックスを何百万回もたどるよりも、すべてをスキャンする方が高速です。
例では、ordersをスキャンし、amount > 100を満たす行を残します。注文の大半が100を超えるなら、seq scanが適切です。
EXPLAIN SELECT * FROM orders WHERE amount > 100;
Seq Scan on orders (cost=0.00..18334.00 rows=900000 width=64)
Filter: (amount > 100)Index Scanの動作
Index ScanはB-treeを使って一致するキーへ直接移動し、その後、対応する行をテーブルのヒープから読み取ります。
フィルターの選択性が高く、テーブルの一部だけを返す場合に威力を発揮します。インデックスを使って5行を検索する方が、1000万行を読み取るより高速です。
プランには使用したインデックスの名前が表示されます。一致する行ごとに、インデックスの検索1回とヒープフェッチ1回(ランダム読み取り)が必要です。そのため、返す行が多くなるとIndex Scanの優位性は失われます。
EXPLAIN SELECT * FROM orders WHERE customer_id = 42;
Index Scan using idx_orders_customer on orders
(cost=0.42..38.50 rows=12 width=64)
Index Cond: (customer_id = 42)選択性が選択を決める
ここでのすべてを左右する中心的な概念が選択性です。これは、述語によって保持される行の割合を指します。
- 選択性が高い(一意のIDのように、一致する行が少ない)場合は、Index Scanが有利です。
- 選択性が低い(
status IS NOT NULLのように、一致する行が多い)場合は、Seq Scanが有利です。
一般的な目安として、クエリがテーブルの約5〜10%を超える行を返す場合、プランナーはシーケンシャルスキャンを選ぶことが多くなります。インデックスを使ったランダムなヒープフェッチの方が、すべてを順番に読み取るより高コストになるためです。
Index-Only Scan
Index-Only Scanは、3つの方式の中で最も高速です。クエリに必要なすべての列がすでにインデックス内にある場合、エンジンはテーブルのヒープに一切アクセスしません。
例のクエリはcustomer_idだけを選択し、それでフィルターしています。また、インデックスもcustomer_idに対して作成されています。必要なデータがすべてインデックスに存在するため、PostgresはIndex Only Scanと報告します。
これにより、通常のIndex Scanを遅くするランダムなヒープ読み取りを回避できます。幅の広いテーブルでは、大きな性能向上になります。
EXPLAIN SELECT customer_id FROM orders WHERE customer_id = 42;
Index Only Scan using idx_orders_customer on orders
(cost=0.42..8.44 rows=12 width=4)
Index Cond: (customer_id = 42)Visibility Mapの注意点
面接官が好んで出す細かなポイントです。Index-Only Scanでも、各行がトランザクションから可視であること(MVCC)を確認する必要がありますが、可視性の情報はインデックスだけには保存されていません。
Postgresはvisibility mapを使用します。ページにall-visibleのマークが付いていればヒープをスキップします。そうでなければ、結局ヒープの行を取得する必要があります。プランにはHeap Fetches: Nと表示されます。
そのため、更新直後のテーブルではヒープフェッチが多くなり、Index-Only Scanが遅くなることがあります。VACUUMを実行してvisibility mapが更新されるまで、この状態が続きます。
Index Only Scan using idx_orders_customer on orders
(actual time=0.01..0.03 rows=12 loops=1)
Heap Fetches: 0Bitmap Scan:中間の選択肢
よく登場する4つ目の方式として、Bitmap Heap Scanがあります。通常のIndex Scanでは扱いたくないほど多く、テーブル全体を読むほどではない行に述語が一致するとき、プランナーはこれを選択します。
まずインデックスから一致する行の位置を示すビットマップを作成し(Bitmap Index Scan)、次にヒープページをランダムな順序ではなく物理的な順序で取得します。順序付けられた取得は、通常のIndex Scanで発生する散在した読み取りよりもはるかに低コストです。
Bitmap Heap Scan on orders (cost=12.0..520.0 rows=8000)
Recheck Cond: (status = 'pending')
-> Bitmap Index Scan on idx_orders_status
(cost=0..12 rows=8000)
Index Cond: (status = 'pending')プランナーがインデックスを無視した理由
面接でよくある質問です。インデックスを追加したのに、プランがSeq Scanのままなのはなぜですか。一般的な理由は次のとおりです。
- 述語の選択性が低いため、スキャンの方が本当に低コストです。
- 列が関数でラップされているためです。
WHERE lower(email) = ...では、emailに対する通常のインデックスを使用できません。 - 型が一致していないため、インデックスを無効にする暗黙的なキャストが発生しています。
- 統計情報が古くなっているためです。
ANALYZEを実行してください。 - テーブルが非常に小さいためです。数ページをスキャンする方が、インデックスのオーバーヘッドよりも低コストです。
診断例
ordersにcreated_atのインデックスがあるのに、次のクエリが依然としてseq scanを実行するとします。
原因はDATE(created_at)です。列を関数でラップすると、元のcreated_atに対するインデックスを使用できません。修正方法は、列をそのままにした範囲条件に書き換えるか、DATE(created_at)に対する式インデックスを作成することです。
-- Slow: function on the indexed column
WHERE DATE(created_at) = '2026-01-01'
-- Fast: bare column, range uses the index
WHERE created_at >= '2026-01-01'
AND created_at < '2026-01-02'方式の比較
面接のために、次の比較を頭に入れておきましょう。
- Seq Scan。行の大部分を返す場合に最適です。I/Oはシーケンシャルです。
- Index Scan。選択性の高い検索に最適です。インデックスをたどった後、ヒープをランダムにフェッチします。
- Bitmap Heap Scan。一致する行数が中程度の場合に適しています。インデックスからビットマップを作成し、その後、ヒープを順序付けて読み取ります。
- Index-Only Scan。必要な列がすべてインデックスに含まれ、ページがすべて可視の場合に最も高速です。
プランナーは、主に選択性と統計情報によって決まる推定コストに基づいて選択します。
テストを強制する方法(本番環境で行わない理由)
開発中に検証するため、プランナーの選択を一時的に誘導することができます。SET enable_seqscan = off;を実行すると、インデックスを優先するよう強制できるため、プランを比較できます。
これは診断のための手法であり、本番環境での修正方法では決してありません。面接では、実際の解決策は統計情報の改善、適切なインデックスの作成、または述語の書き換えであり、プランナーの機能を全体的に無効化することではないと説明してください。
SET enable_seqscan = off;
EXPLAIN ANALYZE SELECT * FROM orders WHERE amount > 100;
SET enable_seqscan = on;確認問題
クエリはemailだけを選択し、emailでフィルターしています。また、emailに対するB-treeインデックスがあります。プランにはIndex Only Scanと表示されています。通常のIndex Scanより高速なのはなぜでしょうか。
まとめ
アクセス方式に関する重要なポイントは次のとおりです。
- Seq Scanは選択性の低いクエリで有利です。Index Scanは選択性の高いクエリで有利です。
- Index-Only Scanは、必要な列がすべてインデックスに含まれている場合にヒープへのアクセスを回避します。
Heap Fetchesとvisibility mapに注意してください。 - Bitmap Heap Scanは、ヒープページを物理的な順序で取得することで、その中間を補います。
- プランナーは選択性と統計情報に基づいて決定します。列に対する関数、型の不一致、古い統計情報が、インデックスが無視される理由です。
よくある質問
「Seq ScanとIndex ScanとIndex-Only Scan」レッスンは無料ですか?
はい。「Seq ScanとIndex ScanとIndex-Only Scan」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、SQL Interview Prepコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 SQL Interview Prepコースには全4レッスンが含まれています。
「Seq ScanとIndex ScanとIndex-Only Scan」で何を学びますか?
プランナーがそれぞれを選ぶ理由と、クエリについて何が分かるかを学びます。 ブラウザで直接実行するハンズオンコードでSQL Interview Prepを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
SQL Interview Prepを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのSQL Interview Prepは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「Seq ScanとIndex ScanとIndex-Only Scan」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このSQL Interview Prepレッスンでコードを書いて実行できますか?
はい。すべてのSQL Interview Prepレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- EXPLAINプランの読み方
- Seq ScanとIndex ScanとIndex-Only Scan
- 結合アルゴリズム:Nested Loop、Hash、Merge
- 遅いクエリの発見と改善