EXPLAIN Query Plan Analyzer

A browser-based EXPLAIN ANALYZE visualizer for Postgres and MySQL plans — indented tree, cost and timing per node, with warnings on the expensive steps.

Paste the output of EXPLAIN or EXPLAIN ANALYZE and this query plan analyzer turns the wall of indented text into a node tree you can actually read. It works as a Postgres EXPLAIN visualizer for both the default text format and FORMAT JSON, and it marks the parts of the plan that usually make queries slow: sequential scans on large tables, nodes where the planned row count is far off the actual one, and inner joins that run thousands of loops. Every node shows its type, the table or index it hits, cost, estimated versus actual rows, and per-node time, so you can see where the plan spends its effort. Reach for it when you have an EXPLAIN plan you can't quite parse by eye and need to decide where an index would earn its keep.

Paste EXPLAIN / EXPLAIN ANALYZE — text or FORMAT JSON
3 nodes 2 flaggedtext
EXPLAIN output
Parsed plan tree
Nested Loop
cost38.145rows (est 5)4.8ms total
Seq Scanon users
cost211200rows (est 4)0.9ms total
Filter: (status = 'active'::text); Rows Removed by Filter: 380
Sequential scan on users reads every row (1200 returned). An index on the filtered column would likely help.
Row estimate off by ~300× (planned 4, actual 1200). Stale statistics — try ANALYZE users.
Index Scanon orders
cost4.271rows (est 1)×1200loops3.6ms total
Index Cond: (user_id = users.id)
Ran 1200 times as the inner side of a join (~3.6 ms total). An index on the join key can cut the loops.

How it works

  1. 1

    Paste your EXPLAIN output

    Run EXPLAIN ANALYZE on the slow query in psql, pgAdmin, or your MySQL client and drop the result into the left pane. Plain text and EXPLAIN (FORMAT JSON) are both accepted — the analyzer detects which one you pasted and shows the format it used.

  2. 2

    Read the node tree

    The right pane redraws the plan as an indented tree, one card per operation. Each card lists the node type, the relation it touches, estimated vs actual rows, loop count, and the time that node accounted for (actual time × loops), so nesting and hot spots are obvious at a glance.

  3. 3

    Fix what's highlighted

    Cards outlined in red are the ones worth attention: a Seq Scan returning thousands of rows, a row estimate off by 10× or more, or an inner node that ran hundreds of loops. The note on each card tells you what to try next — usually an index on the filtered or joined column, or a fresh ANALYZE.

Instant & 100% private — nothing is uploaded

Everything runs locally in your browser. Your code, text and files are processed on your own device and are never sent to a server — so there are no upload waits, no size limits from us, and nothing is ever stored or logged.

Frequently asked questions

What do the cost, rows, and loops numbers in an EXPLAIN plan mean?
cost is the planner's estimate in arbitrary units written as startup..total, rows is how many rows it expected, and loops is how many times the node ran. EXPLAIN ANALYZE adds actual time and actual rows measured during the real run. A node's true contribution is actual time × loops, so an Index Scan reading actual time=0.003 rows=1 loops=1200 quietly cost about 3.6 ms, not 0.003 ms — this tool does that multiplication for you.
How do I spot a bad sequential scan in EXPLAIN ANALYZE?
Look for a Seq Scan whose actual rows is large and that sits under a Filter. For example, `Seq Scan on users (cost=0.00..21.00 rows=4 width=36) (actual time=0.018..0.940 rows=1200 loops=1)` with `Rows Removed by Filter: 380` planned 4 rows but returned 1200 — roughly a 300× estimate miss on a scan that read the whole table. That combination (seq scan, big row skew, rows removed by filter) is the classic sign that an index on the filtered column, followed by ANALYZE, will speed the query up.
Does this visualize MySQL EXPLAIN plans too?
It reads Postgres text and JSON plans in full. MySQL's EXPLAIN ANALYZE prints a similar indented tree with cost and actual-rows figures, so that output parses into the node tree as well. MySQL's older tabular EXPLAIN — the column grid with id, select_type, key, rows — is a different shape and isn't parsed; run EXPLAIN ANALYZE to get the tree form.
What counts as a large gap between estimated and actual rows?
The analyzer flags any node where estimated and actual row counts differ by 10× or more in either direction. A wide gap usually means the planner is working from stale statistics and may be choosing the wrong join order or scan type. Running ANALYZE on the table — or raising the statistics target for a skewed column — is the first thing to try before rewriting the query.