1SELECT * FROM accounts2 WHERE name = 'Kai';3CREATE INDEX by_name4 ON accounts(name);5BEGIN;6UPDATE accounts SET bal=bal-507 WHERE name = 'Ana';8UPDATE accounts SET bal=bal+509 WHERE name = 'Ben';10COMMIT;accounts7Cal906Dee105Ben204Kai603Eve352Ana1001Max75idnamebalDee→ row 6Ana→ row 2Ben→ row 5Cal→ row 7emptyEve→ row 3Kai→ row 4Max→ row 1CRASH
Code card: the SQL; the highlighted lines are the ones runningTable: seven rows (id, name, bal) stored on disk, not sorted by nameIndex: a B-tree of names, each pointing to its rowUndo journal: keeps old values until COMMITFirst, a query for Kai reads all seven rows, because nothing tells the database where Kai is. CREATE INDEX then files every name, with its row id, in a sorted B-tree; the same query walks root → right leaf → row 4. Then 50 moves from Ana to Ben inside a transaction. The old balance goes into the undo journal first, a crash interrupts before Ben is paid, and the journal puts Ana's 100 back. The second try commits both changes together. The drawing shows a tiny classic B-tree. SQLite keeps each table and each index in its own b-tree, and PostgreSQL makes a B-tree when CREATE INDEX names no other type.

Indexes

  1. To find Kai, the database has no shortcut. It reads every row, top to bottom, and checks each name: 7 rows, 7 reads. With a million rows it would read a million. The work grows with n, written O(n).

Transactions

Crash and commit

Try it with Python's sqlite3

-- step 1/12 · lines 1-2
>>> import sqlite3
>>> db = sqlite3.connect("bank.db", isolation_level=None)  # we write BEGIN/COMMIT
>>> db.execute("EXPLAIN QUERY PLAN SELECT * FROM accounts WHERE name = 'Kai'").fetchone()[3]
'SCAN accounts'
>>> db.execute("SELECT * FROM accounts WHERE name = 'Kai'").fetchall()
[(4, 'Kai', 60)]

Real index nodes are disk pages holding hundreds of entries, so the tree stays very shallow: if every node points to 100 children, three levels already reach 100 × 100 × 100 = 1,000,000 rows. SQLite (in its default mode) copies the original content into a rollback journal before it changes the database. PostgreSQL instead keeps a write-ahead log; both let the database recover a consistent state after a crash.