SQL EXPLAIN ANALYZE analyzer
Paste your PostgreSQL EXPLAIN ANALYZE output to get a colour-coded performance tree with slow-node flags, row estimation warnings, and a timing summary.
About this tool
EXPLAIN ANALYZE runs your query and augments the plan tree with actual execution times and actual row counts for every node. This is the most powerful built-in tool PostgreSQL provides for diagnosing slow queries: you can see exactly which node took the most time, whether the planner's row estimates matched reality, and whether expensive operations like sorts or hash builds spilled to disk. This tool parses the EXPLAIN ANALYZE output and renders an annotated tree where slow nodes are flagged automatically. Nodes that took longer than 1 second are marked CRITICAL; those between 100 ms and 1 s are marked SLOW; those between 10 ms and 100 ms are marked SLOW-ISH. Row estimation mismatches — where actual rows were more than 10× higher or lower than the planner estimated — are flagged with a warning icon, because large mismatches are a common root cause of bad plan choices. A summary strip at the top shows planning time, total execution time, and the single slowest node at a glance. All annotation lines (filter predicates, index conditions, removed-row counts, buffer hit/miss data) are collapsible per node so the tree stays readable even for complex plans. Paste the output of EXPLAIN (ANALYZE, BUFFERS) for the richest results.
- 1
Run EXPLAIN ANALYZE <your SQL query> in psql or your PostgreSQL client. For buffer usage, use EXPLAIN (ANALYZE, BUFFERS) <query>.
- 2
Copy the full output, including every indented line and the Planning/Execution Time lines at the bottom.
- 3
Paste it into the input field and click Analyze.
- 4
Review the summary strip for total execution time and the slowest node.
- 5
Nodes flagged CRITICAL or SLOW are candidates for optimization. Look for row estimate mismatches (⚠) as signs of stale statistics.
- 6
Click 'Show details' on any node to see filter predicates, removed-row counts, and buffer data.
Pinpoint which node in a multi-join query is consuming the most time.
Detect stale table statistics when actual row counts differ wildly from estimates.
Confirm that a newly added index is being used and is actually faster.
Share a performance analysis with a colleague without needing database access.
Fast index lookup
Index Scan using users_pkey on users (cost=0.28..8.29 rows=1 width=64) (actual time=0.044..0.045 rows=1 loops=1)
Index Cond: (id = 42)
Planning Time: 0.120 ms
Execution Time: 0.065 msExecution: 0.065 ms — 1 node, index scan, no slow nodesSlow sequential scan
Seq Scan on orders (cost=0.00..1.18 rows=18 width=12) (actual time=0.024..980.234 rows=500000 loops=1)
Filter: (amount > 100)
Rows Removed by Filter: 300000
Planning Time: 0.100 ms
Execution Time: 981.000 msExecution: 981 ms — CRITICAL: Seq Scan returned 500 000 rows vs estimate of 18No actual timing data found
Cause: You pasted the output of plain EXPLAIN rather than EXPLAIN ANALYZE. Plain EXPLAIN shows only estimated costs, not actual times.
Fix: Run EXPLAIN ANALYZE <query> or EXPLAIN (ANALYZE, BUFFERS) <query> in your PostgreSQL client, then paste the result.
No plan nodes found
Cause: The pasted text is not a PostgreSQL EXPLAIN ANALYZE output in the default text format. JSON or XML output is not supported.
Fix: Use the default text format. Run EXPLAIN ANALYZE without FORMAT JSON.
These answers explain common explain analyze tasks, expected input formats, and edge cases so both visitors and search engines can understand what this tool does.
What is EXPLAIN ANALYZE?
EXPLAIN ANALYZE runs the query and measures actual execution time and row counts at each plan node. Unlike plain EXPLAIN, which only shows planner estimates, EXPLAIN ANALYZE shows what really happened during execution.
Why does EXPLAIN ANALYZE run the query?
To get real timing data, PostgreSQL must actually execute the query. For SELECT queries this is safe. For INSERT, UPDATE, or DELETE, wrap the statement in a transaction and roll it back: BEGIN; EXPLAIN ANALYZE UPDATE …; ROLLBACK;.
What does 'loops' mean?
Loops is how many times that node was executed. A nested loop's inner side runs once per outer row, so its total time is actualTotalTime × loops. The time shown per node is the per-loop average.
What causes a row estimation mismatch?
The planner relies on table statistics collected by AUTOVACUUM (or manual ANALYZE). If rows have been inserted, deleted, or updated in bulk since the last ANALYZE, the statistics are stale and estimates can be wildly off. Run ANALYZE <table> to refresh them.
What does EXPLAIN (ANALYZE, BUFFERS) add?
The BUFFERS option adds shared/local buffer hit and miss counts to each node. A high number of block reads (misses) on a node that should hit the cache suggests the table or index is larger than shared_buffers or that the query is I/O bound.