← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Query Plan & EXPLAIN Analysis

performance Intermediate

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

Added 15 Mar 2026
Edited 22 Mar 2026
Views 31
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings F 0 pings S 0 pings S 2 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 2 pings S 0 pings M 0 pings T 2 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S
No pings yet today
Perplexity 9 Amazonbot 7 Google 3 Unknown AI 2 Ahrefs 2 SEMrush 1
crawler 23 crawler_json 1
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

✓ schema.org compliant