Query Plan & EXPLAIN Analysis
Also Known As
EXPLAIN plan
execution plan
query execution plan
TL;DR
The execution strategy the database engine chooses for a query — analysed with EXPLAIN to identify full scans, missing indexes, and bottlenecks.
Explanation
Every SQL query is compiled into an execution plan by the query optimiser. EXPLAIN (MySQL/PostgreSQL) shows this plan: table access type (const, ref, range, index, ALL — ALL is a full table scan and nearly always a problem), estimated row counts, indexes used, join strategy, and temp table/filesort usage. EXPLAIN ANALYZE (PostgreSQL) or EXPLAIN FORMAT=JSON (MySQL 8) shows actual execution statistics. Key red flags: type=ALL on large tables, Using filesort, Using temporary, and high rows_examined vs rows_returned ratios. Run EXPLAIN before adding indexes — the plan tells you exactly which index to create.
Common Misconception
✗ EXPLAIN output is the same across MySQL, PostgreSQL, and MariaDB. The EXPLAIN syntax and output format differ significantly — MySQL uses EXPLAIN FORMAT=JSON or EXPLAIN ANALYZE; PostgreSQL uses EXPLAIN (ANALYZE, BUFFERS). Always consult the docs for your specific database engine.
Why It Matters
The query plan shows exactly how the database executes a query — it reveals full table scans, missing indexes, and row estimates that explain slow performance without guesswork.
Common Mistakes
- Not using EXPLAIN before adding indexes — the plan shows whether an index would actually be used.
- Reading EXPLAIN output without checking actual row counts — estimated vs actual rows reveals stale statistics.
- Not using EXPLAIN ANALYZE (PostgreSQL) or EXPLAIN FORMAT=JSON (MySQL) for full detail.
- Optimising based on EXPLAIN on a small dev dataset — plans change dramatically with production data volumes.
Code Examples
✗ Vulnerable
-- Running slow query without checking the plan:
SELECT * FROM orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'pending';
-- Just add EXPLAIN first:
EXPLAIN SELECT * FROM orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'pending';
-- Look for: Seq Scan (bad on large tables), Nested Loop with high rows
✓ Fixed
-- MySQL EXPLAIN
EXPLAIN SELECT * FROM orders
WHERE user_id = 42 AND status = 'paid'
ORDER BY created_at DESC LIMIT 10;
-- Look for: type (ALL=bad, ref/range=ok, const=best), key (index used), rows estimate
-- PostgreSQL EXPLAIN ANALYZE (actually executes)
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT * FROM orders
WHERE user_id = 42 AND status = 'paid'
ORDER BY created_at DESC LIMIT 10;
-- Look for: Seq Scan on large tables, high rows estimate vs actual, cache hits
-- Paste output into explain.dalibo.com for visual analysis
References
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
15 Mar 2026
Edited
22 Mar 2026
Views
31
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
Perplexity 9
Amazonbot 7
Google 3
Unknown AI 2
Ahrefs 2
SEMrush 1
Also referenced
How they use it
crawler 23
crawler_json 1
Related categories
⚡
DEV INTEL
Tools & Severity
🟠 High
⚙ Fix effort: Medium
⚡ Quick Fix
Run EXPLAIN ANALYZE (PostgreSQL) or EXPLAIN FORMAT=JSON (MySQL) on any slow query — look for sequential scans on large tables, nested loop joins on large sets, and filter rows vs rows examined ratio
📦 Applies To
any
web
cli
🔗 Prerequisites
🔍 Detection Hints
Queries running >100ms without EXPLAIN analysis; SELECT * fetching unused columns; no use of composite indexes the planner would prefer
Auto-detectable:
✓ Yes
mysql-explain
pganalyze
pt-query-digest
explain.dalibo.com
⚠ Related Problems
🤖 AI Agent
Confidence: Medium
False Positives: Medium
✗ Manual fix
Fix: Medium
Context: Function