テック技報最新号へ →
コード

Postgres: INCLUDE 付きのカバリングインデックスで Index Only Scan にする

CREATE INDEX ... ON t (検索列) INCLUDE (取り出す列, ...) とすると、SELECT する列もインデックスに持たせられる。テーブル本体(ヒープ)を読まずにインデックスだけで答えられるので、よく叩かれる絞り込みクエリが速くなる。

@james_ailton · 投稿 2026-11-04
#postgres#index#performance#infra

使う場面

EXPLAIN ANALYZE で Index Scan になっているのに遅く、インデックスで見つけた行ごとにテーブル本体を読みに行っているとき。たとえば「ユーザーの注文一覧で、ID と金額と日時だけ出す」ような、取り出す列が少なく決まっているクエリ。

やり方

sql
-- よく実行されるクエリ
SELECT id, amount, created_at
FROM orders
WHERE customer_id = $1;

-- 検索に使う列はキーに、取り出すだけの列は INCLUDE に
CREATE INDEX CONCURRENTLY orders_customer_covering
  ON orders (customer_id) INCLUDE (id, amount, created_at);

確かめ方:

sql
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, amount, created_at FROM orders WHERE customer_id = 42;
-- Index Only Scan using orders_customer_covering ...
--   Heap Fetches: 0   ← 0 か小さければヒープを読んでいない

注意点

  • Postgres 11 以降の機能。
  • Heap Fetches が多いままなら、可視性マップが古い。Index Only Scan でも「その行が全員から見えるか」を確かめるためにヒープを読みに行く。VACUUM(autovacuum)が追いついているか確認する。更新の多いテーブルほど効果が薄い。
  • INCLUDE の列は検索条件や並び替えには使われない。WHERE や ORDER BY に使う列はキー側に置く。
  • UNIQUE インデックスに INCLUDE を付けても、一意性の判定はキー列だけで行われる。
  • インデックスが大きくなり、書き込みのたびに更新されるコストも増える。全部の列を入れず、本当に頻繁なクエリだけに絞る。
  • 本番では CONCURRENTLY で作る。失敗したときの後始末は PostgreSQL: 失敗した CREATE INDEX CONCURRENTLY は INVALID なインデックスを残す。関数を通した検索には Postgres: lower(email) で検索するなら式インデックスを張る を使う。

出典

X の元の投稿を開く