How to read EXPLAIN ANALYZE and what to look at first

Backend Live Coding November 13, 2026 sql

I read from the inside out.

sql
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
-- ======================================================================
-- Q: How do you read an EXPLAIN ANALYZE? What do you look for first?
--
-- I read from the inside out. I check:
-- - estimated rows vs actual rows: a big difference means stale statistics
--   (ANALYZE) or correlated columns (CREATE STATISTICS).
-- - Seq Scan on a large table with a selective filter: an index is missing or
--   cannot be used (function on the column, cast, LIKE '%x').
-- - Large Rows Removed by Filter: the index covers only part of the WHERE, maybe
--   a composite or partial index is needed.
-- - loops in a Nested Loop: actual time × loops is the real time.
-- - Sort Method: external merge Disk: work_mem is not enough or an index is
--   needed for the ORDER BY.
-- - Buffers shared read vs hit: how many pages came from disk.
-- In practice I start from pg_stat_statements (which queries cost the most in
-- total) and then run EXPLAIN on the specific one.
-- ======================================================================

EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE customer_id = 42 AND status = 'paid'
ORDER BY created_at DESC LIMIT 20;

Limit  (cost=0.43..58.12 rows=20 width=96) (actual time=0.041..0.210 rows=20 loops=1)
  ->  Index Scan using ix_orders_cust_created on orders
        (cost=0.43..1210.55 rows=419 width=96) (actual time=0.040..0.205 rows=20 loops=1)
        Index Cond: (customer_id = 42)
        Filter: (status = 'paid')
        Rows Removed by Filter: 3
        Buffers: shared hit=24
Planning Time: 0.180 ms
Execution Time: 0.240 ms