venish.dev
← writing
postgresql / postgres-index-only-scans

When Postgres Chooses an Index-Only Scan

published 2026-06-081 min read143 words

An index-only scan answers a query from the index alone, never touching the heap. Postgres will only do it when two conditions hold.

Condition one: the index covers the query

Every column referenced — in SELECT, WHERE and ORDER BY — must be present in the index. INCLUDE adds payload columns without widening the search key:

covering index
CREATE INDEX idx_orders_customer
  ON orders (customer_id)
  INCLUDE (status, total_cents);

Condition two: the visibility map is current

The index holds no row visibility information, so Postgres consults the visibility map to learn whether a page is all-visible. Pages dirtied since the last vacuum are not, and those rows require a heap fetch anyway.

Reading the plan

EXPLAIN (ANALYZE, BUFFERS) reports Heap Fetches. Zero means the scan was genuinely index-only. A large number after a bulk load means the table has not been vacuumed — run VACUUM ANALYZE and measure again before touching the index.