b2KIT

SQL EXPLAIN Visualizer

Paste EXPLAIN output from PostgreSQL or MySQL and see a visual query plan with cost estimates.

Tested tool guide Tested browser tools Checked August 16, 2026

What SQL EXPLAIN Visualizer does, with a checked example

EXPLAIN prints a query plan as indented text, which is what you stare at when debugging a slow query. This tool turns that text into a tree diagram: each node is one plan step - a seq scan, an index scan, a join - labeled with startup cost, total cost, and estimated rows. Indentation becomes nesting, so a Bitmap Index Scan beneath a Bitmap Heap Scan renders as a child node. The common surprise: cost numbers are arbitrary planner units, not milliseconds, and unless you ran EXPLAIN ANALYZE, every value is an estimate derived from table statistics.

Worked example

A concrete input and expected output from the current implementation.

Input

Bitmap Heap Scan on tenk1  (cost=5.07..229.20 rows=100 width=244)
   Recheck Cond: (unique1 < 100)
   ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..5.04 rows=100 width=0)
         Index Cond: (unique1 < 100)

Expected output

A two-node tree. The root card reads "Bitmap Heap Scan on tenk1", cost 5.07..229.20, rows=100; its single child reads "Bitmap Index Scan on tenk1_unique1", cost 0.00..5.04, rows=100. The child carries Index Cond: (unique1 < 100); the parent shows Recheck Cond: (unique1 < 100). The parent's startup cost (5.07) sits just above the child's total cost (5.04).

PostgreSQL plans build bottom-up: the heap scan cannot emit a row until the index scan has produced its bitmap, so the parent's startup cost is the child's total cost plus a small amount (5.07 versus 5.04). The tool draws that dependency as the parent-child edge.

How the result is produced

1

Parsing either dialect

The pasted text is identified as PostgreSQL or MySQL: PostgreSQL plans are indented trees with cost=(startup..total), rows, and width annotations, while MySQL plans are flat tables with id, select_type, table, type, key, and rows columns. Per-node numbers are extracted, and the indentation depth of each PostgreSQL line becomes tree nesting.

2

Building the diagram

Each parsed node becomes a card labeled with the operation and table name, showing total cost and estimated rows, and parent-child steps are joined by edges. Because every ancestor's cost includes its descendants' costs, the tree's shape makes the dominant work - the subtree holding most of the total plan cost - visible at a glance.

Good uses

  • After rewriting a slow query, paste EXPLAIN before and after to confirm the plan actually changed - for example, that a new index turned a Seq Scan into an Index Scan - instead of just trusting the rewrite.
  • A teammate reports a slow report; you paste the plan and point at the offender, a Seq Scan over a large table or a Nested Loop with rows=50000, and the visual makes the problem explainable in one sentence.
  • When rows=1 appears on a node over a table with millions of rows, you have found stale statistics: run ANALYZE, re-EXPLAIN, and compare the two trees to watch the plan recover.

Limits and checks

  • Cost units are arbitrary. 229.20 does not mean 229 milliseconds; costs are comparable only across plans for the same query in the same database and say nothing about wall-clock time.
  • Everything shown is the planner's estimate. If ANALYZE has not run since the data changed, row estimates can be off by orders of magnitude and the whole plan shape can be wrong.
  • EXPLAIN without ANALYZE carries no actuals. For real row counts and times, paste EXPLAIN ANALYZE output instead, and treat a large gap between estimate and actual as the actual finding.

Common questions

The plan says cost 229 but the query took three seconds. Is the tool wrong?

No. Cost is an arbitrary unit the planner uses to compare candidate plans, not a time prediction. Actual duration depends on hardware, caching, and concurrent load, so no visualizer converts cost to time. Use this tool to compare plans, and EXPLAIN ANALYZE for measured timings.

Can it tell me why the planner chose this join order?

Only indirectly. The tool shows the plan that was chosen, not the alternatives that were rejected. Re-run EXPLAIN with planner switches disabled - enable_seqscan=off, enable_hashjoin=off - and paste those plans in too; comparing the trees is the standard way to expose what the planner was weighing.

References and verification

The example and behavioral notes were checked against the browser implementation. Standards and primary references below define the relevant format, formula, or platform behavior.

Related Tools