Reading Postgres EXPLAIN ANALYZE: The 2026 Guide
A field guide to Postgres EXPLAIN ANALYZE in 2026. The operators that matter, the numbers to look at, and the four patterns that explain 90% of slow queries.
EXPLAIN ANALYZE is the single most useful Postgres tool, and the one most teams use timidly. Past 2024 the output format settled, the operators got fully documented, and AI assistants can read the trees for you. In 2026 it's the obvious starting point when anything is slow.
The shape of an EXPLAIN output
Every plan is a tree of nodes. Each node has a type (Seq Scan, Index Scan, Hash Join, etc.), an estimated cost, an actual time, and an actual row count.
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT u.id, u.email, count(p.id) AS post_count
FROM users u
LEFT JOIN posts p ON p.author_id = u.id
WHERE u.created_at > now() - interval '7 days'
GROUP BY u.id, u.email
ORDER BY post_count DESC
LIMIT 10;Read the tree bottom-up. Each node feeds its parent. The interesting nodes are usually the leaves and the joins.
Operators that matter
- Seq Scan: reads every row in a table. Fine on small tables, a problem on big ones. If you see this on a table over a few thousand rows in a hot query, you need an index.
- Index Scan: uses an index. Fast. Good.
- Index Only Scan: uses an index AND doesn't need to fetch the heap. Even better. Requires every column referenced to be in the index (covering index).
- Bitmap Heap Scan: uses an index but in a different way; Postgres builds a bitmap of matching pages then reads them. Common when several conditions can use indexes that combine via AND/OR.
- Nested Loop: joins by iterating one side and looking up the other. Fast for small driving tables. Catastrophic for big ones.
- Hash Join: builds a hash on one side, scans the other. Good for big-to-big.
- Merge Join: requires both sides sorted. Cheap if you happen to have indexes that sort the right way; otherwise the sort step adds time.
- Sort: ordering rows. Expensive if it doesn't fit in
work_mem(you'll see "external merge Disk" in the plan).
Numbers that lie
The trap most beginners fall into: the planner's cost estimate.
Cost is a unitless number Postgres uses internally to pick the cheapest plan. It's not seconds, not microseconds. It correlates loosely with reality. Don't compare costs across queries; only within a single query's alternatives.
What actually matters: actual time, rows (compare estimated vs actual: if they're wildly different, your statistics are stale), and loops (a node with loops=1000 ran 1000 times, even if each ran fast).
Four patterns that explain 90% of slow queries
1. Seq Scan on a big table
Missing index. Add one on the column(s) in the WHERE clause. For composite predicates, the order matters: the most selective column first.
2. Nested Loop with huge outer rows
The planner expected a small driving table; it got a big one. Either the statistics are stale (run ANALYZE) or you need a different join strategy. Add an index on the inner table's join column.
3. Sort spilling to disk
work_mem is too small for the sort. Either increase it for that session, or arrange to use a presorted index, or limit the result set before sorting.
4. Estimated vs actual rows wildly different
Stale statistics. Run ANALYZE table_name. If it persists, look at default_statistics_target for that column or add multi-column statistics with CREATE STATISTICS.
BUFFERS is your friend
Add BUFFERS to your EXPLAIN ANALYZE. It tells you how many 8KB pages the query read from cache vs disk. A query that reads 1M buffers is doing a lot of I/O regardless of how cleverly the plan looks.
EXPLAIN (ANALYZE, BUFFERS) SELECT ...shared hit means cached; shared read means from disk; shared dirtied means a write happened in the query. The ratio of hit to read tells you whether you have a memory problem or a query problem.
Once you can read EXPLAIN ANALYZE fluently, you can fix most performance problems in minutes instead of hours. For the bigger picture of when to add what, see our indexes guide.
Suparbase is an admin workspace for Supabase. Encrypted credentials, server-side proxy, RLS debugger, SQL playground, AI assistant with diff-confirmed writes. Free tier for solo projects.
Related articles
- postgres · indexes
Postgres Indexes Explained, Visually: btree, GIN, BRIN, GiST, Hash
Every Postgres index type, what it does, when to use it. A 2026 visual guide with concrete examples and the surprising performance numbers.
Read article - postgres · mvcc
MVCC in Postgres: When It Bites You
Postgres uses MVCC for concurrency. Most of the time you don't think about it. Then you do. Here are the four ways MVCC bites in production, and how to handle each.
Read article - postgres · observability
The Modern Postgres Observability Stack in 2026
The metrics that actually matter, the tools that work in 2026, and the alerts to set up before your database becomes someone else's problem.
Read article