Overview
Prerequisites
- Prior topics: 10 · SQL Essentials --
SELECT,JOIN,GROUP BY/HAVING,INSERT/UPDATE/DELETE, foreign keys, basic transactions (COMMIT/ROLLBACK), and the first Pythonsqlite3examples this topic assumes as a floor and then extends into a real client-server engine; 4 · Just Enough Python -- the type-annotated Python fluency this topic's data-access-layer examples assume; 11 · Backend Essentials -- the N+1 query scenario that topic introduces at the application layer, which this topic's co-26 (N+1 diagnosis and fix, Examples 54-56) diagnoses and resolves from the database side. - Tools & environment: a macOS/Linux terminal; PostgreSQL 18.x (the
psqlclient and a running server -- Docker'spostgres:18image is the simplest way to get one); Python 3.x withpsycopg(v3,pip install "psycopg[binary]") installed in avenv; Example 76 additionally needspsycopg-pool(pip install psycopg-pool). - Assumed knowledge: comfort writing and running
SELECT/JOIN/GROUP BYqueries and basic Python scripts (topic 10). No prior exposure to window functions, query planning, or transaction isolation is assumed -- this topic is where those begin.
Why this exists -- the big idea
The problem before the solution: correct SQL can still be catastrophically slow -- the query
that flew on 100 rows melts at 10 million, and you cannot see why without the planner.
Keep-this-if-you-forget-everything: the database does what you ask, well or badly; EXPLAIN is
how you see the how, and an index is a space-and-write-cost bargain you make to buy read speed.
Cross-cutting big ideas, taught here and then reused for the rest of this curriculum:
consistency-latency-throughput -- isolation levels and locking trade correctness guarantees
against concurrency and speed; abstraction-and-its-cost -- indexes and denormalization buy reads
by charging writes and storage.
Confirm your toolchain
Every example in this topic runs against a real, running PostgreSQL 18 server:
$ psql --version
psql (PostgreSQL) 18.4
$ psql -U asqp -d asqp -c "SELECT version();"
PostgreSQL 18.4 on aarch64-unknown-linux-musl, compiled by gcc ...Every SQL example is a complete, self-contained, runnable .sql file colocated under
learning/code/, and every Python example is a complete, self-contained, runnable .py file --
both actually executed against a real PostgreSQL 18.4 instance (via Docker, postgres:18.4-alpine,
with shared_preload_libraries = 'pg_stat_statements' for Example 82) to capture the documented
output. Every query result, EXPLAIN plan, timing number, and server log excerpt on this topic's
pages is a genuine, captured transcript, never a fabricated one -- including the exact PostgreSQL 18
plan-output details that changed from prior versions (Buffers: shown by default under EXPLAIN ANALYZE, fractional row counts on loop-averaged estimates, Index Searches: and Heap Fetches:
lines, and the new B-tree skip-scan capability).
How this topic's examples are organized
- Beginner (Examples 1-28) -- uncorrelated and correlated subqueries, derived
tables, common table expressions (
WITH) including a first recursive counter and tree walk, the four core window-function building blocks (running total, partitioned average, ranking functions,LAG/LEAD),NTILEquartiles,UNION/INTERSECT/EXCEPTset operations,GROUP BY ROLLUPand grouping sets,FILTER-based conditional aggregation, transactions (BEGIN/COMMIT/ROLLBACK) and a genuine atomicity-failure demonstration, creating a B-tree index,EXPLAINandEXPLAIN ANALYZEbasics,ANALYZErefreshing planner statistics,FOR UPDATErow locking and the default Read Committed isolation level (both via real two-session Python scripts), andpsql's\timing. - Intermediate (Examples 29-64) -- rewriting a correlated subquery as a
join, cycle-safe recursive CTEs over graphs and a bill-of-materials explosion, moving averages,
explicit
RANGEvs.ROWSwindow frames, theFIRST_VALUE/LAST_VALUEdefault-frame gotcha,PERCENT_RANK/CUME_DIST, top-N-per-group,LATERALjoins,CUBEcrosstabs, every specialized index type PostgreSQL ships (composite, covering, partial, expression, hash, GIN, BRIN) with the write-cost and bloat tradeoffs each one carries, all threeEXPLAINjoin-node types (nested loop withMemoize, hash join, merge join) forced deterministically for teaching,Buffersin a real plan, a stale-statistics misestimate, the N+1 query problem diagnosed and fixed two ways in Python, every classic read anomaly PostgreSQL's isolation levels can and cannot produce (non-repeatable read, PostgreSQL's stronger-than-standard phantom-read prevention, write skew, and a genuineSerializationFailureretry), a real reproduced deadlock and its consistent-ordering fix, advisory locks, and a plain materialized view refresh. - Advanced (Examples 65-85) -- a recursive-CTE shortest path over a weighted
graph, gaps-and-islands window sessionization, a measured window-function-vs-self-join
performance gap (687x on this data), a multi-
LATERALdashboard report, deliberate covering-index design,CREATE STATISTICSfor correlated columns, declarative range partitioning with verified pruning and a measured bulk-delete tradeoff against a single big index, denormalization measured honestly in both directions (reads faster, writes costlier), a non-blockingREFRESH ... CONCURRENTLYproven via real thread timing, a connection-pooling benchmark, the full isolation-level anomaly matrix side by side,SERIALIZABLE's measured bookkeeping overhead and real retry rate under contention, buffer-driven I/O tuning, a genuine planner-cost-constant plan flip, slow-query-log triage,pg_stat_statementstop-N ranking (with its non-default setup prerequisites stated explicitly), OLTP-normalized vs. OLAP star-schema query shape,COPYvs. row-INSERTbulk-load throughput, and a closing example that threads a window report, index tuning, an N+1 fix, and a concurrency anomaly's reproduction-and-resolution into one workflow.
The 28 concepts this topic covers
- co-01 · Subqueries and derived tables -- correlated vs. uncorrelated subqueries and subqueries
used as derived tables in
FROM. - co-02 · Common table expressions --
WITHfactoring a query into named, readable steps. - co-03 · Recursive CTEs --
WITH RECURSIVEwalking hierarchies and graphs with a termination guard. - co-04 · Window functions --
OVER()computing across a row set without collapsing rows into groups. - co-05 · Window frames and partitions --
PARTITION BY/ORDER BY/ theROWSvs.RANGEframe clause. - co-06 · Ranking and analytic functions --
ROW_NUMBER/RANK/DENSE_RANK,LAG/LEAD,NTILE,PERCENT_RANK. - co-07 · Set operations --
UNION/INTERSECT/EXCEPTand theirALLvariants. - co-08 · Grouping sets, ROLLUP, CUBE -- multi-level aggregation (subtotals, grand totals, crosstabs) in one pass.
- co-09 · LATERAL joins --
LATERALcorrelating a subquery to each row of the left side. - co-10 · Conditional aggregation --
FILTER (WHERE ...)andCASE-based aggregates for pivots. - co-11 · ACID properties -- atomicity, consistency, isolation, and durability defined concretely.
- co-12 · MVCC -- multi-version concurrency control: readers see a snapshot and don't block writers.
- co-13 · Isolation levels -- Read Committed, Repeatable Read, and Serializable and the anomalies each forbids.
- co-14 · Read phenomena -- dirty read, non-repeatable read, phantom, and write skew as named anomalies.
- co-15 · Serializable snapshot isolation -- PostgreSQL SSI detecting dangerous dependency structures and aborting.
- co-16 · Explicit locking -- row locks (
FOR UPDATE/FOR SHARE), advisory locks, and lock modes. - co-17 · Database deadlocks -- how the engine detects a lock cycle and how consistent lock ordering avoids it.
- co-18 · B-tree index mechanics -- the sorted-tree index and why equality, range, and ordering all benefit.
- co-19 · Composite and covering indexes -- column order, index-only scans, and
INCLUDEcolumns. - co-20 · Specialized indexes -- hash, GIN, GiST, and BRIN indexes and the workload each fits.
- co-21 · Partial and expression indexes -- indexing a predicate subset or a computed expression.
- co-22 · Index cost tradeoff -- write amplification, bloat, and when an index hurts more than it helps.
- co-23 · EXPLAIN and EXPLAIN ANALYZE -- estimated vs. actual plans; PostgreSQL 18 shows buffer stats by default.
- co-24 · Reading a query plan -- scan and join node types (nested loop / hash / merge), cost, rows, width.
- co-25 · Table statistics and ANALYZE -- the planner depends on
ANALYZEstats; stale stats cause bad plans. - co-26 · N+1 diagnosis and fix -- the app-side query explosion and its join / batch fixes.
- co-27 · Denormalization and materialized views -- trading write cost and storage for read speed.
- co-28 · Partitioning, pooling, and OLTP vs. OLAP -- declarative partitioning, connection pooling, and workload-shaped schema.
Examples by Level
Beginner (Examples 1–28)
- Example 1: Uncorrelated Subquery
- Example 2: Correlated Subquery
- Example 3: Derived Table in FROM
- Example 4: Simple CTE
- Example 5: Multi-Step CTE
- Example 6: Recursive CTE Counter
- Example 7: Recursive CTE Tree
- Example 8: Window Running Total
- Example 9: Window Partition Avg
- Example 10: Row Number vs Rank vs Dense Rank
- Example 11: Lag/Lead Delta
- Example 12: NTILE Quartiles
- Example 13: UNION vs UNION ALL
- Example 14: INTERSECT and EXCEPT
- Example 15: GROUP BY ROLLUP
- Example 16: Grouping Sets
- Example 17: FILTER Aggregate
- Example 18: Conditional SUM (CASE Pivot)
- Example 19: BEGIN, COMMIT, and ROLLBACK
- Example 20: Atomicity Failure
- Example 21: Create B-tree Index
- Example 22: EXPLAIN Basic
- Example 23: EXPLAIN ANALYZE Basic
- Example 24: Seq Scan vs Index Scan
- Example 25: ANALYZE Refresh Stats
- Example 26: FOR UPDATE Row Lock
- Example 27: Read Committed Default
- Example 28: psql \timing
Intermediate (Examples 29–64)
- Example 29: Correlated Subquery to Join
- Example 30: Recursive CTE Graph Cycle
- Example 31: Recursive CTE Bill of Materials
- Example 32: Window Moving Average
- Example 33: RANGE vs ROWS Frame
- Example 34: FIRST_VALUE and LAST_VALUE
- Example 35: PERCENT_RANK and CUME_DIST
- Example 36: Top-N per Group
- Example 37: LATERAL Join Top-N
- Example 38: LATERAL vs Correlated Subquery
- Example 39: CUBE Crosstab
- Example 40: Composite Index Order
- Example 41: Covering Index and Index Only Scan
- Example 42: Partial Index
- Example 43: Expression Index
- Example 44: Hash Index
- Example 45: GIN Index on jsonb
- Example 46: BRIN Index on Timeseries
- Example 47: Index Hurts Writes
- Example 48: Index Bloat, Observed
- Example 49: EXPLAIN Nested Loop
- Example 50: EXPLAIN Hash Join
- Example 51: EXPLAIN Merge Join
- Example 52: Buffers in the Plan
- Example 53: Stale Stats, Bad Plan
- Example 54: N+1 Reproduce
- Example 55: N+1 Fix, JOIN
- Example 56: N+1 Fix, IN Clause
- Example 57: Repeatable Read Anomaly (Prevented)
- Example 58: Phantom Read (Prevented by PostgreSQL)
- Example 59: Write Skew
- Example 60: Serialization Failure, Retry
- Example 61: Deadlock, Reproduce
- Example 62: Deadlock, Avoid via Consistent Ordering
- Example 63: Advisory Lock
- Example 64: Materialized View Refresh
Advanced (Examples 65–85)
- Example 65: Recursive CTE, Shortest Path
- Example 66: Window Sessionization
- Example 67: Window vs Self-Join Performance
- Example 68: LATERAL Cross-Apply Report
- Example 69: Covering Index Design
- Example 70: Multicolumn Statistics
- Example 71: Partition by Range
- Example 72: Partition Pruning, EXPLAIN
- Example 73: Partition vs Index, Bulk-Delete Tradeoff
- Example 74: Denormalization, Measured
- Example 75: Materialized View, CONCURRENTLY
- Example 76: Connection Pooling Benchmark
- Example 77: Isolation Level Matrix
- Example 78: Serializable Throughput Cost
- Example 79: EXPLAIN Buffers, I/O Tuning
- Example 80: Planner Cost Constants
- Example 81: Slow Query Log, Triage
- Example 82: pg_stat_statements, Top-N
- Example 83: OLTP-Normalized vs OLAP Star Schema
- Example 84: Bulk Load, COPY vs INSERT
- Example 85: Capstone Preview, Tuning
← Previous: 25 · Advanced Algorithms Drilling · Next: Beginner Examples →
Last updated July 16, 2026