Index Cond と Filter の違い(読む前に効く条件・読んだ後に捨てる条件)
Index Cond はインデックスを辿る段階で使われる条件で、読む行数そのものを減らす。Filter は行を読んだ後に捨てるための条件で、読む量は減らない。Rows Removed by Filter がその無駄読みの量を示す。
同じ条件が、2 か所のどちらかに出る
WHERE に書いた条件は、実行計画ではIndex Cond 側か Filter 側かのどちらかに現れます。 この 2 つは効くタイミングが違います。
Index Cond— インデックスを辿る段階で使う。読む行数そのものが減るFilter— 行を読んだあとに捨てる。読む量は減らない
同じクエリでも、インデックスの張り方でどちらに出るかが変わります。実際に並べます。
まず、2 つが並んで出ている計画を見る
id(主キー・インデックスあり)とgrade(インデックス無し)で絞ります。
SELECT * FROM members WHERE id BETWEEN 1000 AND 1200 AND grade = 'gold';
Index Scan using members_pkey on members (cost=0.42..14.26 rows=71 width=34) (actual time=0.012..0.028 rows=67.00 loops=1)
Index Cond: ((id >= 1000) AND (id <= 1200))
Filter: (grade = 'gold'::text)
Rows Removed by Filter: 134
Index Searches: 1
Planning Time: 0.292 ms
Execution Time: 0.054 msIndex Cond と Filter が同じノードに並んで出ている。
PostgreSQL 18.6 / 2026-08-22 採取 / 並列・JIT は OFF
2 つが上下に並んでいます。
Index Cond: ((id >= 1000) AND (id <= 1200))— インデックスを辿る段階で使われた条件Filter: (grade = 'gold')— 行を読んでから判定した条件Rows Removed by Filter: 134— 201 行読んで134 行を捨てた(返したのは 67 行)
id の側はインデックスが範囲を直接絞れるので、そもそも 201 行しか読んでいません。grade の側はインデックスが無いので、201 行すべてを読んでから 134 行を捨てています。これが「読む前に効く」と「読んだあとに捨てる」の違いです。
スキャンの種類によって、条件の出る場所が変わります。上の Index Scan では Index Cond と Filter が同じノードに並びますが、Bitmap Heap Scan になると Index Cond は 子ノードの側に移ります。次がその形です。
Bitmap になると、条件が 2 つのノードに分かれる
-- members(city) にだけインデックスがある SELECT * FROM members WHERE city = 'city-7' AND grade = 'gold';
Bitmap Heap Scan on members (cost=113.01..4431.51 rows=3346 width=34) (actual time=0.697..9.094 rows=3333.00 loops=1)
Recheck Cond: (city = 'city-7'::text)
Filter: (grade = 'gold'::text)
Rows Removed by Filter: 6667
Heap Blocks: exact=4167
-> Bitmap Index Scan on members_city_idx (cost=0.00..112.17 rows=10100 width=0) (actual time=0.417..0.417 rows=10000.00 loops=1)
Index Cond: (city = 'city-7'::text)
Index Searches: 1
Planning Time: 0.202 ms
Execution Time: 9.276 msIndex Cond は子の Bitmap Index Scan 側にある。
PostgreSQL 18.6 / 2026-08-22 採取 / 並列・JIT は OFF
条件が 2 つのノードに散っています。探すときは子まで見てください。
- 子の
Bitmap Index ScanにIndex Cond: (city = 'city-7')— これが本物のIndex Cond。 インデックスを辿って「どのページを読むか」を決めている - 親の
Bitmap Heap ScanにRecheck Cond: (city = 'city-7')— 同じ条件をもう一度書いたもの。 ビットマップが粗くなったときに備えた確認用で、「読む前に効いている」側という点はIndex Condと同じ Filter: (grade = 'gold')とRows Removed by Filter: 6667— こちらは変わらず読んだあとに捨てている側
10,000 行読んで 3,333 行しか使っていません。3 分の 2 が無駄読みです。
両方を含む複合インデックスを張ると
CREATE INDEX members_city_grade_idx ON members (city, grade);
Bitmap Heap Scan on members (cost=47.00..4234.56 rows=3373 width=34) (actual time=0.452..3.415 rows=3333.00 loops=1)
Recheck Cond: ((city = 'city-7'::text) AND (grade = 'gold'::text))
Heap Blocks: exact=3333
-> Bitmap Index Scan on members_city_grade_idx (cost=0.00..46.15 rows=3373 width=0) (actual time=0.242..0.243 rows=3333.00 loops=1)
Index Cond: ((city = 'city-7'::text) AND (grade = 'gold'::text))
Index Searches: 1
Planning Time: 0.237 ms
Execution Time: 3.518 msFilter が消え、両方の条件が読む前に効くようになった。
PostgreSQL 18.6 / 2026-08-22 採取 / 並列・JIT は OFF
Filter の行が消えました。代わりにRecheck Cond(と子の Index Cond)に両方の条件が乗っています。Rows Removed by Filter も出ていません。捨てる行がゼロになったということです。
効果は Heap Blocks の行に出ています。これは実際に読んだページ数です。
- 単一インデックス:
Heap Blocks: exact=4167 - 複合インデックス:
Heap Blocks: exact=3333
4,167 ページ → 3,333 ページ。20% 減りました。捨てる行が無くなったぶん、触るページも減っています。
読む量が減った結果は、実行時間にも出ています。同じ計画の Execution Time が 9.3 ミリ秒 → 3.5 ミリ秒(2.6 倍)。 ページを 20% 減らしただけに見えて、行を捨てる処理そのものが無くなっているぶん差が大きく出ます。 複合インデックスの列順の話は複合インデックスに。
3 つを並べると、捨てている量の差が見える
Index Cond に乗せられた条件が増えるほどここが短くなり、 複合インデックスではゼロになる。棒の長さはそろえてあるので、比べているのは行数ではなく捨てた割合。実際の行数は棒の右に出ている。Rows Removed by Filter の読み方
この数字が「無駄読みの量」です。大きければ大きいほど、インデックスを見直す価値があります。
ただし絶対値では判断できません。1 万行捨てていても、それが全体の 1% なら気にする必要はありません。返した行数と並べて見ます。
- 返した行 3,333 / 捨てた行 6,667 → 3 分の 2 が無駄。直す価値がある
- 返した行 100 万 / 捨てた行 1,000 → 気にしなくてよい
ループの内側に出ることもあります。遅いノードの見つけ方の題材では、 内側のノードに Rows Removed by Filter: 4 が出ています。1 回あたり 4 行という意味なので、loops を掛けると 100 万行捨てていることになります。loops が付いているときは、この数字も 1 回あたりです。
インデックスが効いているかの判定にも使える
「インデックスを貼ったのに速くならない」ときは、まず条件が Index Cond 側に乗っているかを見ます。Filter 側に残っていれば、そのインデックスはその条件には効いていません。
乗らない理由はいくつもあります(列に関数をかけている、型が合っていない、 複合インデックスの左端を使っていない、など)。 一覧はインデックスが使われないときに何を見るかに まとまっています。
よくある疑問
関連トピック
もっと学びたい方へ(おすすめ書籍)
「なぜこの書き方が速いのか」を実行計画から説明する一冊。条件分岐・集約・結合・更新のそれぞれで、良い書き方と悪い書き方を対比しながら読める。
テーブル設計と正規化、パフォーマンス考慮のインデックス設計まで実務レベルで学べる定番書。第2版ではクラウド対応も強化。
ER 図をどう「使える設計」に落とすか、実務の判断まで踏み込んだ入門書。エンティティの切り出しから多対多の扱いまで具体例が豊富。
ドリル 256 問を実際に打ちながら進める SQL の入門書。付属のブラウザ環境で演習できるので、SELECT から結合・集約までを環境構築で止まらずに通せる。
SQLの本質的な使い方と、インデックスが効くクエリの書き方を学べる。ウィンドウ関数など現代SQLも網羅。
実務でやりがちなSQL・DB設計のアンチパターンとその回避策を体系的に学べる。
PostgreSQLの内部構造・ストレージ・インデックス機構を丁寧に解説。設計と運用計画の鉄則が学べる。
IPAデータベーススペシャリスト試験の総合対策書。インデックス関連は本サイトと合わせて学ぶと理解が深まる。
リレーショナルモデルの理論から、インデックス設計を含む実務で使えるSQLまで解説。
本セクションはAmazonアソシエイトのリンクを含みます。
もっと深くDBを学びたい方へ。
たいてっくが、SQL・データベース設計・パフォーマンスチューニング・IPAデータベーススペシャリスト対策まで、1対1で学習をサポートします。まずは無料相談から。
「教え方も上手で、お人柄も良いメンターです。DB周りの知識はもちろん、何より、しっかり教えてあげようという姿勢がとてもありがたかったです。データベース、SQLの学習を考えている方にはおススメです。」
— H 様(DB・SQL コース受講)「体系的に知識を教えてくださり、実際の業務でも大変役立っております。特に短い時間で効率よく知識の習得や、練習をできているのは期待以上でした。」
— M 様(DB・SQL コース受講)「大変充実したコンテンツでわかりやすいご説明をありがとうございました。基本的な質問にも丁寧にご説明いただき、また業務のご相談にも乗って頂き大変有意義な時間でした。」
— K 様(DB・SQL コース受講)