4.4.15 · D1Databases

Foundations — EXPLAIN — reading query plans, cost estimation

2,860 words13 min readBack to topic

This page assumes you have seen nothing. Every word, letter, and symbol the parent note leans on is unpacked below, each one building on the one before it.


0. What is a "row", a "table", and a "page"?

Before any cost or symbol makes sense, picture where the data physically lives.

What this figure shows: three white boxes labelled page 1, 2, 3, each holding five thin magenta horizontal lines. Each thin line is one row (tuple); each box is one page that packs many rows together. Orange arrows lead from one page to the next, and the label reminds you that counts the pages while counts all the rows across every page. The picture makes concrete why we track two numbers: rows live inside pages.


1. The two letters and

The parent note writes formulas using and . Here is what they are.

Recall Why does the topic need both

and instead of one? Because two different costs are involved ::: reading data is paid per page (), while filtering and emitting is paid per row () — so both counts appear in the formula.


2. Sequential vs random: the picture behind two page costs

The whole cost model hinges on one physical fact about disks.

What this figure shows: a horizontal strip of eight numbered page-boxes. Along the bottom, one straight magenta arrow sweeps left-to-right through pages 1,2,3,… — that is sequential access, priced per page. Along the top, curved violet arrows hop between pages in the jumbled order 2 → 6 → 1 → 5 — that is random access, priced per page because each jump pays a seek penalty. The orange caption drives home that it is the same 8 KB page either way; only the route to it changes.


3. The planner's four price tags (the constants)

The parent note's formulas multiply things by little numbers like , , . These are the planner constants — fixed prices the optimizer uses.


3b. The recipe: writing an actual cost formula

Now assemble the constants into the formula the parent note uses. Take the simplest operator, a sequential scan (read the whole table, test a one-operator filter on each row):


4. What "cost" means and its two flavours

The plan prints cost=0.00..18334.00. Two numbers, separated by two dots.

What this figure shows: the string cost = 0.00 .. 18334.00 written large. A magenta arrow points at the left number (0.00) labelled startup cost — work before the FIRST row; a violet arrow points at the right number (18334.00) labelled total cost — work to the LAST row. Below, a short timeline runs from a magenta dot ("first row out") to a violet dot ("last row out"), showing that the two cost numbers mark the two ends of the query's output stream.


5. Selectivity — the fraction

The parent uses , , and . Build them now.


6. Where the numbers come from — statistics


6b. The hidden discount — buffer cache

The cost recipe above treats every page read as a real trip to disk. Reality is kinder.


7. The tree shape — leaves, nodes, indentation

Here is a concrete two-node plan — read the indentation as "who feeds whom":

Aggregate  (cost=30840.00..30840.01 rows=1)      <- runs LAST (final output)
  -> Seq Scan on orders  (cost=0.00..30834.00 rows=20)   <- runs FIRST (reads data)
       Filter: (amount > 500)

The indented Seq Scan at the bottom runs first: it reads pages and emits the 20 matching rows upward to its parent. The Aggregate on top runs last: it receives those rows and collapses them into one count. Notice the parent's cost () includes the child's cost () plus a little more — costs accumulate as rows flow up.

Building blocks used inside the tree draw on B-Tree Indexes (fast lookups) and Join Algorithms — Nested Loop, Hash, Merge (combining tables).


How the foundations feed the topic

A plain-English map of what depends on what (read each arrow as "is needed for"):

  1. Page = disk chunk → gives you (pages) and (tuples), the two counts.
  2. Sequential vs random access → justifies the four cost constants ().
  3. Counts + constants → let you write the cost formula and split it into startup vs total.
  4. + uniform-spread assumption → give selectivity and the row count .
  5. Statistics from ANALYZE → supply the numbers that feed .
  6. Cost formula + selectivity + tree of operators → together let you read EXPLAIN output and judge whether the planner chose well.

The same dependencies as a Mermaid diagram (for readers who use one):

Page = disk chunk

P pages and T tuples

Sequential vs Random

Four cost constants

Cost formula and startup vs total

n_distinct and uniform spread

Selectivity s and rows S

Statistics from ANALYZE

Reading EXPLAIN output

Tree of operators


Equipment checklist

Test yourself — reveal only after answering.

  • What is a page, and why is cost counted in pages? ::: A page is a fixed-size (~8 KB) disk chunk holding many rows; disk reads whole pages, so page-reads are the real unit of I/O cost.
  • What do and stand for? ::: = number of pages in the table; = number of tuples (rows).
  • Why is random_page_cost (4.0) bigger than seq_page_cost (1.0)? ::: A random page needs a seek/jump each time, which is about four times slower than streaming the next adjacent page.
  • Write the sequential-scan total cost formula. ::: , with startup .
  • How many operator evaluations does amount > 500 AND status = 'active' charge per row? ::: Two comparisons (one >, one =), so in the CPU-operator term.
  • What are the two numbers in cost=0.00..18334.00? ::: Startup cost (before ..) = work before the first row; total cost (after ..) = work to produce the last row.
  • Why does a Sort have high startup cost but a Seq Scan near-zero? ::: A sort must read and order all rows before emitting the first; a scan can emit row 1 immediately.
  • Define selectivity and the row count . ::: is the fraction of rows a predicate keeps (); is the actual number of rows kept.
  • Why does equality selectivity use ? ::: Assuming values spread uniformly, each distinct value covers about of the rows.
  • Why can a query with a high printed cost= still run fast? ::: Cost assumes disk reads, but pages already in the buffer cache / OS cache are served from RAM at near-zero I/O cost.
  • In which direction does a plan tree execute? ::: Bottom-up / inside-out — the most-indented leaves run first and feed their parents; the top line is the final output.