Lesson 24 / 25

Reading EXPLAIN ANALYZE

What actually happened.

Plans are trees, read bottom-up

EXPLAIN shows the planner's chosen plan; EXPLAIN ANALYZE runs the query and adds actual rows and loops (plus timings unless disabled). Read the tree from the innermost nodes upwards: scans feed joins, which feed sorts and aggregates. Look for big differences between estimated and actual rows, sequential scans on large tables with selective filters, and "Rows Removed by Filter". Use BUFFERS to see I/O.

An aggregated join, analysed, run

I ran this with psql on PostgreSQL 16.2 against the course sample data (shown in the first section). Run with COSTS OFF, TIMING OFF and SUMMARY OFF so the output is stable. Orders are filtered to 5 paid rows (2 removed), hashed, joined to 4 customers, sorted and grouped into 3 rows. Memory figures depend on the build and data.

EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT c.name, count(*)
FROM customers c JOIN orders o ON o.customer_id = c.id
WHERE o.status = 'paid'
GROUP BY c.name;

Output:

                              QUERY PLAN                              
----------------------------------------------------------------------
 GroupAggregate (actual rows=3 loops=1)
   Group Key: c.name
   ->  Sort (actual rows=5 loops=1)
         Sort Key: c.name
         Sort Method: quicksort  Memory: 25kB
         ->  Hash Join (actual rows=5 loops=1)
               Hash Cond: (c.id = o.customer_id)
               ->  Seq Scan on customers c (actual rows=4 loops=1)
               ->  Hash (actual rows=5 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 9kB
                     ->  Seq Scan on orders o (actual rows=5 loops=1)
                           Filter: (status = 'paid'::text)
                           Rows Removed by Filter: 2
(13 rows)

Never EXPLAIN ANALYZE a destructive query casually

It really executes the statement; wrap UPDATE or DELETE in BEGIN ... ROLLBACK.

Quick check: What does EXPLAIN ANALYZE add compared with EXPLAIN?

  • It creates missing indexes
  • It rewrites the query
  • It executes the query and reports actual row counts
  • It only formats the SQL
Answer

It executes the query and reports actual row counts — Estimated versus actual.