Curriculum
Seven tracks. Every lesson runs against a seeded database, and every claim in the prose is a query you can edit and re-run without leaving the page.
Foundations
7 lessons · 10 drillsThe parts everyone thinks they know. Three-valued logic, what a join does with the rows that found no partner, and why COUNT(*) and COUNT(col) are different numbers.
- 1SELECT, and what a column really is8 min
A select list is a list of expressions, not a list of columns. Once that lands, half of SQL stops being a special case.
- 2NULL, and the third truth value14 min
NULL is not a value. It is the absence of one, and every comparison it touches returns neither true nor false. Half of all real SQL bugs live here.
- 3Joins, as sets and as loops12 min
A join is a nested loop with a condition. Every join type is a rule about what to do with the rows that found no partner.
- 4Grouping, and the two ways to count12 min
COUNT(*) counts rows. COUNT(col) counts values. They are different numbers, and knowing which one you asked for is most of the skill.
- 5Subqueries, and when they are secretly a loop12 min
A subquery that mentions the outer query runs once per outer row. One that does not runs once. The difference does not show in the SQL.
- 6Set operations, and what they cost8 min
UNION removes duplicates and UNION ALL does not — which is not a stylistic difference. One of them has to remember every row it has already emitted.
- 7The order clauses actually run in10 min
Written in one order, evaluated in another. Almost every “why can’t I use my alias there” question is answered by this, and so are several performance ones.
Modelling
2 lessons · 1 drillNormalisation to the point where it stops paying. What a foreign key costs on write. Surrogate against natural keys, soft deletes, and time zones stored wrong.
- 1Constraints are correctness, not decoration12 min
A constraint is the one guarantee that survives every bug in every application that will ever talk to this database. It also has a cost, and the cost is on the write path.
- 2What a data JSONB column costs14 min
Every AI-written schema has one. It is not free, it is not indexed, and the planner has no idea what is inside it — and the plan will say all three out loud.
Indexes
3 lessons · 4 drillsSelectivity, composite column order and the left-prefix rule, covering indexes, partial and expression indexes, and the corollary that a function around a column throws the index away.
- 1What an index is, and what it costs12 min
A B-tree is a sorted structure with a real height. Reading it is cheap; going back to the table for each row it found is not, and that is the whole story.
- 2Column order, and the index that answers on its own12 min
An index on (a, b) can seek on a, and an index on (b, a) cannot. And an index that carries every column the query wants never touches the table.
- 3Functions, and the index that only holds some rows10 min
Wrapping a column in a function throws its index away, because the index stores the column and you asked about something else.
The planner
3 lessons · 3 drillsEstimated against actual. The three join algorithms and when each wins. Stale statistics, correlated predicates, and the case where the planner is right and the query is wrong.
- 1Reading a plan14 min
A plan is a tree of numbers. Estimated against actual is one column, and it localises more slow queries than everything else put together.
- 2Where the estimate comes from12 min
ANALYZE takes a snapshot. Everything the planner knows is from that moment, and the gap between it and now is where bad plans come from.
- 3The three join algorithms12 min
Nested loop, hash and merge. Each wins somewhere, and the plan tells you which one you got and what it cost.
Transactions
3 lessons · 0 drillsIsolation levels and what each costs. Lost updates and the SELECT … FOR UPDATE that prevents them. Deadlock as a wait-for graph, and the bloat a long transaction leaves behind.
- 1What a transaction actually guarantees12 min
Not that nothing else happens. Only that your work lands all at once or not at all — and that what you read stays still for as long as your isolation level says it does.
- 2The four anomalies, and what each level costs16 min
Dirty read, non-repeatable read, phantom, and write skew. Postgres never permits the first, permits the next two at READ COMMITTED, and refuses all four at SERIALIZABLE.
- 3Locks, deadlock, and what a long transaction leaves behind14 min
Writers block writers. Two of them waiting for each other is a deadlock, and the fix is lock ordering. And any open transaction stops the whole table from being cleaned up.
Production
4 lessons · 2 drillsKeyset against offset pagination, measured. Migrations that lock and the patterns that do not. Connection pools, and EXPLAIN triage as a procedure.
- 1The N+1, from the database side12 min
One query for the list and one for each row. From the application it looks like a loop; from the database it looks like a thousand identical statements, and the plan shows you the loops.
- 2Pagination, at page 1 and at page 20,00012 min
OFFSET is not a seek. It reads every row it skips, so page 20,000 costs twenty thousand pages more than page 1 — and the last page is the slowest thing your app does.
- 3Migrations that lock, and the ones that do not12 min
Most of what makes a migration dangerous is which lock it takes and for how long. A few of them take a lock that stops every read on the table, and they look identical in the file.
- 4EXPLAIN triage, as a procedure10 min
It is three in the morning and something is slow. Here is the order to look in, and what each answer rules out.
Generated SQL
1 lesson · 1 drillThe thesis track. The ORM's N+1, the SELECT * in a hot path, the IN list with four thousand elements, the migration with no CONCURRENTLY, and the query that is correct and takes ninety seconds.
- 1A query an agent wrote14 min
It compiles, it returns the right rows on your laptop, and it passes review because the reviewer read the SQL and not the plan. The thesis track.
23 lessons written so far. The topics still to come are listed in the repository’s handoff document rather than stubbed here — a thin lesson is worse than an honest gap.