Course 5 of 6 · f1db, chinook, geoname

Query Optimization Fundamentals

Systematic bottleneck identification, query rewriting, index strategy, and verified improvements.

8Modules
6Core
1Advanced
1Architect
Module 01

How PostgreSQL Executes Queries

Core · 4 topics

The planner generates a plan tree from statistics and cost estimates. This module covers pg_stats, row-count estimates, join ordering, and how cost model parameters shape planning decisions.

Book reference: Chapter 9 — Indexing Strategy

01
Cost model settings (seq_page_cost, random_page_cost)Tune cost model settings to match your server's actual storage performance.
02
Table statisticsUnderstand how PostgreSQL estimates row counts before a query runs.
03
Row estimates from EXPLAINSpot row-count estimates in query plans and trace them back to statistics.
04
Query lifecycle statistics with pg_stat_statementsFind which queries consume the most cumulative time in your database.

Free preview: Reading EXPLAIN

Query execution pipeline
Trace the path a query takes from parsing to execution.
Module 01 · Cost model parameters
select name,
       setting,
       unit,
       short_desc
  from pg_settings
 where name in (
   'seq_page_cost',
   'random_page_cost',
   'cpu_tuple_cost',
   'cpu_index_tuple_cost',
   'cpu_operator_cost',
   'effective_cache_size',
   'work_mem'
 )
 order by name;

Name, setting, unit, and description for seven planner cost and memory parameters.

Module 02

Reading and Interpreting EXPLAIN

Core · 4 topics

EXPLAIN reveals startup cost, total cost, estimated rows, and row width for every node. FORMAT JSON and the indentation model are covered alongside how to read a multi-node plan tree.

Book reference: Chapter 9 — Indexing Strategy

01
Basic EXPLAINRead cost and row estimates for every step of a query plan.
02
EXPLAIN ANALYZECompare estimated vs. actual rows to catch planner misestimates.
03
BUFFERSIdentify which plan nodes generate the most disk I/O.
04
Plan node types: Seq Scan, Index Scan, Hash Join, Nested Loop, Merge JoinRecognize scan and join node types and predict how they scale.

Free preview: Reading EXPLAIN

EXPLAIN anatomy
Read the four key numbers at every plan node.
Module 02 · EXPLAIN ANALYZE join
explain (analyze, buffers, format text)
select r.name    as race,
       d.surname as winner
  from f1db.races   r
  join f1db.results res
    on res.raceid   = r.raceid
  join f1db.drivers d
    on d.driverid   = res.driverid
 where r.year = 2017
   and res.positionorder = 1
 order by r.date;

Actual and estimated rows, timing, and buffer hits for a three-table race-winners join.

Module 03

Identifying Performance Bottlenecks

Core · 4 topics

pg_stat_statements ranks queries by cumulative total time — the right starting point for any optimization session. EXPLAIN ANALYZE adds actual rows and timing; BUFFERS shows cache hits vs disk reads.

Book reference: Chapter 9 — Indexing Strategy

01
Sequential scan cost on large tablesEstimate the cost of a full table scan before running any query.
02
Misestimation from correlated columnsFix row-count misestimates caused by columns that are not statistically independent.
03
Sort costSpot when an unindexed sort is blocking early result delivery.
04
Join selectivity and predicate orderingOrder join predicates to minimize the number of intermediate rows processed.

Free preview: Reading EXPLAIN

Estimated vs actual row counts
Spot estimate divergence and trace it to a statistics problem.
Module 03 · Correlated column misestimate
-- geoname.class and .feature are strongly
-- correlated: 'PPL' always implies class 'P'.
-- The planner multiplies selectivities as
-- if they were independent and lands far
-- below the actual row count.
explain (analyze, buffers)
select name, population
  from geoname.geoname
 where class   = 'P'
   and feature = 'PPL';

EXPLAIN ANALYZE plan for geoname rows matching both class 'P' and feature 'PPL'.

Module 04

Query Rewriting Techniques

Core · 4 topics

Predicate pushdown, CTE fences, and subquery flattening change what the planner can see. Restructuring a query to remove barriers often yields more improvement than any index change.

Book reference: Chapter 9 — Indexing Strategy

01
Predicate pushdownRewrite queries so filters are applied as early as possible.
02
EXISTS vs IN for subqueriesChoose the right subquery form to avoid unnecessary row materialization.
03
CTE optimization fencesControl whether a CTE is a planning barrier or a transparent subquery.
04
Removing unnecessary work: DISTINCT, COUNT semanticsRemove hidden deduplication work that adds overhead without changing results.

Free preview: Query Rewriting & Anti-Patterns

Plan tree showing predicate pushdown
See where filters land in the plan tree.
Module 04 · Predicate pushdown
-- Inline predicates give the planner freedom
-- to push them to the earliest scan.
explain (analyze, buffers)
select r.name, r.date,
       d.surname as winner
  from f1db.races   r
  join f1db.results res
    on res.raceid        = r.raceid
   and res.positionorder = 1
  join f1db.drivers d
    on d.driverid        = res.driverid
 where r.year = 2017;

Race names, dates, and 2017 winners with all predicates placed inline in the join.

Module 05

Common Anti-Patterns

Core · 3 topics

Leading-wildcard LIKE, implicit type coercion, function-wrapped indexed columns, and NOT IN with nullable columns — each prevents index use in ways that are easy to miss in code review.

Book reference: Chapter 9 — Indexing Strategy

01
Non-sargable predicatesSpot predicates that silently defeat index use and know how to fix them.
02
OFFSET pagination vs keyset paginationImplement pagination that stays fast regardless of which page you request.
03
SELECT * and row width impactLimit column selection to what queries actually need.

Free preview: Query Rewriting & Anti-Patterns

B-tree and non-sargable predicates
See how a predicate form can defeat index access.
Module 05 · Keyset pagination
-- Keyset: seek straight to the last key seen.
-- The B-tree descends once and reads exactly
-- one page of rows — page 5000 costs the
-- same as page 1.
explain (analyze, buffers)
select geonameid, name
  from geoname.geoname
 where geonameid > 10150618
 order by geonameid
 limit 20;

Twenty geoname rows starting after a known geonameid boundary, ordered by geonameid.

Module 06

Indexing for Performance

Core · 7 topics

Partial indexes, covering indexes, expression indexes, and multi-column index column order. Index-only scans eliminate heap access entirely — when they're possible and when the visibility map blocks them.

Book reference: Chapter 9 — Indexing Strategy

01
Index types: B-tree, Hash, GiST, GIN, BRINChoose the right index type for your data shape and access pattern.
02
Selectivity and index choicePredict when the planner will choose an index over a full table scan.
03
Composite indexes and column orderDesign multi-column indexes that your queries can actually use.
04
Partial indexesCreate indexes that cover only the rows your queries actually filter on.
05
Covering indexes with INCLUDE clauseBuild covering indexes that satisfy queries without touching the table.
06
Index-only scans and the visibility mapUnderstand when a query can be answered entirely from the index.
07
GiST kNN scan and GIN array containmentIndex geometric distances and array membership for specialized query patterns.

Free preview: Indexing Strategy

Index types
Match the index variant to the query it needs to serve.
Module 06 · Partial index targeting
-- This query scans all results to find
-- race winners — a partial index on
-- positionorder = 1 would eliminate it.
--
-- CREATE INDEX ON f1db.results (raceid)
-- WHERE positionorder = 1;
explain (analyze, buffers)
select res.raceid,
       res.driverid,
       res.constructorid
  from f1db.results res
 where res.positionorder = 1
 order by res.raceid;

Raceid, driverid, and constructorid for every race winner, ordered by raceid.

Module 07 · Advanced

Advanced Optimization Patterns

Advanced · 3 topics

Parallel query configuration, partition pruning, and BRIN for sequential data. This module covers when to reach for pg_hint_plan and when to fix the statistics instead.

Book reference: Chapter 9 — Indexing Strategy

01
Tables and views inventory (pg_class)Inspect relation sizes to guide caching and materialization decisions.
02
Materialized CTEs (WITH … AS MATERIALIZED)Cache an intermediate result when the same data is needed multiple times.
03
Query decomposition with named CTEsDecompose complex queries into readable stages without blocking the optimizer.

Free preview: Query Rewriting & Anti-Patterns

Parallel plan nodes: Gather and Gather Merge
Identify parallel and partition-aware nodes in a plan tree.
Module 07 · Career statistics profiling
explain (analyze, buffers)
select d.surname,
       d.nationality,
       count(distinct r.circuitid)  as circuits,
       count(*) filter(
         where res.positionorder=1) as wins,
       sum(res.points)              as career_pts,
       min(r.year)                  as first_year,
       max(r.year)                  as last_year
  from f1db.drivers      d
  join f1db.results      res
    on res.driverid  = d.driverid
  join f1db.races        r
    on r.raceid      = res.raceid
  join f1db.circuits     c
    on c.circuitid   = r.circuitid
 group by d.driverid, d.surname,
          d.nationality
 order by career_pts desc
 limit 20;

Top 20 drivers ranked by career points, with win count, circuits visited, and active years.

Module 08 · Architect

Systematic Optimization Workflow

Architect · 8 topics

Observe → Diagnose → Fix → Verify: a repeatable cycle for closing the feedback loop on query performance. Performance SLAs, regression guards, and continuous monitoring with pg_stat_statements.

Book reference: Chapter 9 — Indexing Strategy

01
Step 1 — Observe: pg_stat_statements, pg_stat_user_tablesBuild a prioritized list of queries to optimize from real runtime data.
02
Step 2 — Diagnose: plan signals (filter removal, spill, batches, estimate divergence)Read plan warning signals and link each one to a specific root cause.
03
Step 3 — Fix: ANALYZE, index creation, query rewrite, work_mem, VACUUMApply one targeted fix at a time and measure its effect before continuing.
04
Step 4 — Verify: plan comparison, pg_stat_statements confirmationConfirm each optimization actually improved the plan before moving on.
05
Partitioning and partition pruningLeverage table partitioning to skip irrelevant data at planning time.
06
Parallel query: Gather, Gather MergeEnable parallel execution for large scans and understand how results are merged.
07
Performance budgets, query SLAs, caching ladderSet performance targets per query and detect regressions before they reach users.
08
When not to optimizeKnow when a query is good enough and when to stop optimizing.

Free preview: Reading EXPLAIN · Query Rewriting & Anti-Patterns · Indexing Strategy

Optimization workflow
Follow each step of a repeatable optimization cycle.
Module 08 · Partition inventory
select parent.relname  as parent_table,
       child.relname   as partition_name,
       pg_size_pretty(
         pg_relation_size(child.oid)
       )               as partition_size
  from pg_inherits
  join pg_class parent
    on parent.oid = pg_inherits.inhparent
  join pg_class child
    on child.oid  = pg_inherits.inhrelid
  join pg_namespace n
    on n.oid      = parent.relnamespace
 where n.nspname = 'f1db'
 order by parent.relname, child.relname;

Every f1db partition's name and physical size, grouped by parent table.

All 6 courses. One package.

Core, Advanced, and Architect tiers — 48 modules across 6 PostgreSQL courses.

Get All 6 Courses Browse All 6 Courses