Query plan / EXPLAIN

The database's step-by-step route for running a query, showing scans, indexes, joins, estimates, and the work that is actually slow.

why did the database choose a slow queryshow how this sql will runis my index actually being usedexlpain querycan someone read this explain output for mewhy is this doing a sequential scanthe query is instant locally and crawls in productionwhat does cost=0.00..1234.56 mean

See it

Live demo coming soon

What it is

A query plan is the database's chosen route from SQL to rows: which table to read first, whether to scan or use an index, how to join, and where to sort or aggregate. PostgreSQL's EXPLAIN shows the estimated plan. EXPLAIN ANALYZE actually runs the statement and adds real row counts and timings, which is how you see where the estimate and reality parted ways.

Reach for it before adding an index or rewriting a slow query. Read the tree from the deepest nodes outward, then compare estimated rows with actual rows and loops: a node with loops in the thousands is doing the same work over and over. EXPLAIN (ANALYZE, BUFFERS) adds shared buffer hits and reads, so you can also see how much of that work touched cache versus disk. Nobody reads a large plan as raw text for long: run EXPLAIN (ANALYZE, FORMAT JSON) and paste the output into explain.dalibo.com or explain.depesz.com, which highlight the expensive node and the worst row estimate for you.

Gotcha: cost is the planner's internal score, not milliseconds, and a sequential scan is not automatically bad when most of a table is needed. Large estimate errors are often the more useful clue because they can send the planner toward the wrong join or scan. Also remember that EXPLAIN ANALYZE executes the statement, including INSERT, UPDATE, and DELETE, so test write plans inside a transaction you roll back.

Ask AI for it

Diagnose this PostgreSQL query with EXPLAIN (ANALYZE, BUFFERS, VERBOSE, FORMAT JSON), with track_io_timing on so buffer reads carry real timings. Capture the plan on representative production-scale data and annotate each scan, join, sort, row estimate, actual row count, loop count, and buffer total. Identify the first node where estimates diverge sharply or work multiplies, then propose the smallest query, statistics, or B-tree index change that addresses that node. Run ANALYZE after any statistics change and show before and after plans. For INSERT, UPDATE, or DELETE, wrap EXPLAIN ANALYZE in BEGIN and ROLLBACK so the diagnostic does not keep the data changes.

You might have meant

indexjoinn 1 querycursor paginationconnection pool