id name year1 Dune 19652 Coraline 20023 Holes 19984 The Road 20065 Wonder 20126 Matilda 19887 Piranesi 2020sepsep123SELECT name FROM booksWHERE year = 2006;CREATE INDEX year_idxON books (year);SELECT name FROM booksWHERE year BETWEEN 1990AND 2010 ORDER BY year;INSERT INTO booksVALUES (7, 'Piranesi',2020);EXPLAIN SELECT nameFROM booksWHERE year = 2006;Seq Scan on books Filter: (year = 2006)Index Scan using year_idx on books Index Cond: (year = 2006)19651988199820022006201220061990202020122012
Left: the books table and the SQL cardRight: the index, a B-tree of pages (root, leaves)Pucks: index entries (year) on the moveLeft: the books table, in id order, and the SQL card. Right: an index on year, a small B-tree with a root page of separator keys, sorted leaf pages of (year, row) entries, pointers down and links across. Front: the EXPLAIN card. Without the index the DBMS reads every row. With it, a lookup goes root, leaf, row; a range goes down once and walks right along the leaves; a new key that finds its leaf full splits it. The figure’s leaves hold 3 entries. A PostgreSQL page is 8 kB, and an entry for an integer key takes about 20 bytes, so a full leaf holds about 407. A million rows then need only 3 levels: a root, one inner level and the leaves.

Scan or index

  1. The app asks for books from 2006. With no index, the DBMS cannot know where those rows are, so it reads every row and tests its year: a sequential scan. 6 rows here; in a big table, millions.

Searching the tree

The price of an index

What happens

;; step 1/10 · scan
SELECT name FROM books
WHERE year = 2006;

plan: Seq Scan on books
  row 1  1965 = 2006?  no
  row 2  2002 = 2006?  no
  row 3  1998 = 2006?  no
  row 4  2006 = 2006?  yes  keep
  row 5  2012 = 2006?  no
  row 6  1988 = 2006?  no
6 rows read to find 1
the work grows with the size of the table

The figure’s leaves hold 3 entries. A PostgreSQL page is 8 kB, and an entry for an integer key takes about 20 bytes, so a full leaf holds about 407. A million rows then need only 3 levels: a root, one inner level and the leaves. PostgreSQL picks plans from cost estimates. On a table of six rows it would read the one table page (Seq Scan); the Index Scan on the card is the plan for the same query on a large table. SQLite uses the index even here.