Overview
A sequential scan (Seq Scan in EXPLAIN) reads every heap page and filters each row. It needs no index, and its cost grows linearly with table size. Seeing one in a plan is a prompt to check table size and selectivity, not an automatic reason to add an index.
Rules
- The planner rightly picks it for small tables and for queries that return a large fraction of rows. It is a problem when it returns a tiny fraction of a large table.
- Unexpected Seq Scans usually mean stale statistics (run
ANALYZE), an index with the wrong column order or operator class (for exampleLIKEwithouttext_pattern_ops), or a type mismatch or function call on the indexed column inWHERE. - To test whether an index would help,
SET enable_seqscan = offin one session,EXPLAIN, thenRESET. Never set it globally. - Find hot spots with
pg_stat_user_tables.seq_scanandseq_tup_read.
SELECT relname, seq_scan, seq_tup_read, idx_scan
FROM pg_stat_user_tables ORDER BY seq_tup_read DESC LIMIT 10;Related
- query-plan: how the planner chooses between scans.
- btree-index: the usual way to avoid one.
- gin-index: replaces sequential text and JSONB scans.
- vacuum: keeps the visibility map and statistics current.
- postgres-explain: reading scan nodes.
- postgres: the Postgres reference.