statsplannerindex1SELECT *2FROM tenk13WHERE unique1 = 42WHERE unique1 < 7000tenk1 · 345 pagesA · Seq Scancost 470.00B · Index Scancost 8.30cost > 470.00Index Scancost=0.29..8.30rows=1 width=244actual rows=1Seq Scancost=0.00..470.00rows=7000 width=2441 rowstats42
Back row: statistics, the planner, the index and the table's pagesFront: the query, plan A, plan B and the EXPLAIN outputMovers: the query, the row estimate, the index probe, the row, the winner's tickAt the back: the statistics the planner reads, the planner, the B-tree index tenk1_unique1 and the table tenk1 (its 345 pages drawn as 15 tiles). In front: the query, the two candidate plans and the EXPLAIN output. For unique1 = 42 the index plan is far cheaper (8.30 against 470); for unique1 < 7000 reading the whole table in order wins. The costs are the ones printed in the PostgreSQL 17 documentation for its tenk1 test table, with the default cost settings. Your server may print slightly different numbers: statistics come from samples, and costs depend on the version and platform.

The planner's choice

  1. This query asks for the row of tenk1 whose unique1 is 42. It says what to find, not how to find it. Before anything runs, a part of PostgreSQL called the planner must choose how.

Reading a plan

When the best plan changes

What happens

;; step 1/10 · query
the app sends:
  SELECT *
  FROM tenk1
  WHERE unique1 = 42

tenk1   10,000 rows; unique1 holds 0 .. 9999,
        each value once, in no particular order
tenk1_unique1   a B-tree index on unique1

the planner must choose HOW to find the row

The costs are the ones printed in the PostgreSQL 17 documentation for its tenk1 test table, with the default cost settings. Your server may print slightly different numbers: statistics come from samples, and costs depend on the version and platform. Which table page holds unique1 = 42 depends on how the rows were loaded; the highlighted page in the figure is illustrative.