SQL Boy
TutorialsPlayground

Format & Validate

SQL FormatterSQL MinifierSyntax Validator

Convert

JSON to SQLCSV to SQLSQL to JSONRegex to SQLExcel to SQLSQL Dialect Convertersoon

Visualize

ER Diagram GeneratorSQL Schema Diff

Generate

SQL Mock Data Generator

Analyze

SQL Query ExplainerSQL Query Analyzer

Workflow hubs

FormattingConversionSchemaAnalysis
View all tools
Daily ChallengeInterviewsCheat SheetBlog

© 2026 SQL Boy. Built for developers.

ContactPrivacyTermsAI Usage PolicyEditorial StandardsAboutRSS Feed

New conversation

What can I help with?

Ask about this page, get a hint on a challenge, or explore SQL concepts.

I know what's on this page and can give answers grounded in SQL Boy content.

Current page

Reading Sql Execution Plans

/blog/reading-sql-execution-plans

Usage status will load after login
SQL Boy
TutorialsPlayground

Format & Validate

SQL FormatterSQL MinifierSyntax Validator

Convert

JSON to SQLCSV to SQLSQL to JSONRegex to SQLExcel to SQLSQL Dialect Convertersoon

Visualize

ER Diagram GeneratorSQL Schema Diff

Generate

SQL Mock Data Generator

Analyze

SQL Query ExplainerSQL Query Analyzer

Workflow hubs

FormattingConversionSchemaAnalysis
View all tools
Daily ChallengeInterviewsCheat SheetBlog
Back to Blog
Published 2025-12-13
8 min read

How to Read SQL Execution Plans: A Beginner’s Guide

sqlperformanceoptimizationquery-tuningexplain

Author

SQL Boy Team

Editorial Team at SQL Boy

This article is maintained as part of SQL Boy's hands-on SQL library.

We aim to keep examples runnable, call out dialect differences, and revise unclear sections over time.

Read editorial standardsAbout SQL BoyRequest a correction

If a result or dialect note looks wrong, email [email protected] with the article URL and the section you want reviewed.

You've written a SQL query. It returns the right results. But it takes forever to run.

You assume you need an index, or maybe you need to rewrite the join. But without looking under the hood, you're just guessing.

To truly optimize a query, you need to understand how the database executes it. That's where the Execution Plan comes in. It's the roadmap the database engine generates to fetch your data, and reading it is a superpower every developer needs.

Execution plan tree with scan and join nodes
Execution plan tree with scan and join nodes

What is an Execution Plan?

When you submit a SQL query, the database doesn't just run it immediately. First, the Query Optimizer analyzes your SQL and determines the most efficient way to execute it. It considers:

  • Which tables to query first?
  • Should it use an index or scan the whole table?
  • How should it join the tables (Nested Loop, Hash Join, Merge Join)?

The result of this analysis is the Execution Plan.

Using EXPLAIN

In most SQL databases (PostgreSQL, MySQL, SQLite), you see the plan by prepending EXPLAIN to your query.

EXPLAIN SELECT * FROM users WHERE id = 100;

For even more detail, like actual run times, use EXPLAIN ANALYZE (in Postgres) or EXPLAIN QUERY PLAN (in SQLite).

Key Concepts: Scans vs. Seeks

The most fundamental thing to look for is how the database finds rows.

1. Full Table Scan (The Slow One)

The database reads every single row in the table to find matches. This is fine for tiny tables but disastrous for large ones.

Look for keywords like: SCAN TABLE, Seq Scan, Full Table Scan.

2. Index Scan / Seek (The Fast One)

The database uses an index to jump directly to the rows you need, like using the index at the back of a book.

Look for keywords like: SEARCH TABLE, Index Scan, Index Seek.

Interactive Demo: Scan vs. Index

Let's see the difference in action. We'll create a table with 10,000 rows. First without an index, then with one.

Interactive SQL
Loading...

What to watch for:

  1. In the first output, look for SCAN TABLE. That's bad for large datasets.
  2. In the second output, look for SEARCH TABLE ... USING INDEX. That's much better!

Understanding Join Methods

When you join tables, the optimizer has to decide how to match rows.

Nested Loop Join

Great for small datasets or when one side is very small. It iterates through the first table and looks up matches in the second table one by one. Analogy: For every person in Room A, go ask everyone in Room B if they are friends.

Hash Join

Better for larger datasets. It builds a hash table (lookup map) in memory for one table and then scans the other to find matches. Analogy: Make a list of everyone in Room A. Then let everyone in Room B walk by and check the list.

Estimated Plan vs Actual Execution

One of the biggest beginner mistakes is assuming the plan is a literal replay of what happened. In most databases, there are two related but different views:

  • Estimated plan: what the optimizer expects to do based on statistics.
  • Actual plan: what really happened when the query ran, including row counts and execution timing.

That difference matters because many performance problems come from bad estimates, not just bad SQL syntax.

If the optimizer thinks a filter will return 10 rows but it actually returns 200,000 rows, it may choose a join method or index access pattern that becomes painfully expensive in practice.

As a rule:

  1. Start with EXPLAIN to understand the shape of the query.
  2. Move to EXPLAIN ANALYZE or your database's runtime plan view when the query is still slow.
  3. Compare estimated rows versus actual rows to spot misestimation.

When those numbers are wildly different, the fix may involve rewriting the predicate, updating statistics, or changing the indexing strategy rather than just "adding an index somewhere."

Common Plan Nodes You Should Recognize First

You do not need to memorize every possible node type. In practice, a few recurring signals explain most slow queries:

Sort

A sort node appears when the database must reorder rows for ORDER BY, merge joins, or grouped results.

This is not always bad, but it becomes expensive when:

  • a large number of rows survive the filters
  • the sort spills to disk instead of staying in memory
  • the query sorts rows only to discard most of them later

If you repeatedly see expensive sorts, check whether an index could satisfy the filter and the ordering together.

Aggregate

Aggregates such as COUNT, SUM, and AVG often show up as Aggregate, HashAggregate, or similar nodes. The real question is not "is aggregation slow?" but "how many rows survived long enough to be aggregated?"

If an aggregate is expensive, the real fix often sits earlier in the plan:

  • filter earlier
  • reduce join fan-out
  • avoid duplicate-producing joins
  • make grouping keys index-friendly when possible

Bitmap Scan or Index Scan Variants

Different engines use slightly different names, but the principle is the same: the optimizer may combine index results, scan an index and then visit the table, or use a two-step access path when a pure seek is not possible.

The important thing is not the exact label. It is whether the engine is:

  • narrowing the candidate rows efficiently, or
  • reading a huge percentage of the table anyway

Nested Loop Warning Sign

Nested loops are not automatically bad. They are often excellent when one side is tiny and the lookup side is indexed.

They become dangerous when:

  • the outer side is much larger than expected
  • the inner lookup has no useful index
  • the query repeats an expensive operation once per row

This is why row estimates matter so much. A nested loop that is perfect for 50 rows can be disastrous for 500,000 rows.

Best Practices for Query Tuning

Based on execution plans, here is how you fix slow queries:

  1. Eliminate Full Table Scans: If you see a scan on a large table in a WHERE or JOIN clause, consider adding an index on that column.
  2. Watch for High Costs: Plans usually show a "cost" number. While relative, highly skewed costs indicate the bottleneck.
  3. Be Careful with Functions: WHERE YEAR(date_col) = 2023 often prevents index usage (making it "non-sargable"). Use WHERE date_col >= '2023-01-01' AND date_col < '2024-01-01' instead.
  4. Select Only What You Need: SELECT * can prevent "Index Only Scans" (where the DB finds data solely in the index without touching the main table).

A Repeatable Plan-Reading Workflow

If you are staring at a complicated plan and do not know where to start, use this sequence:

  1. Start at the widest or most expensive node. That is usually where the query spends most of its time.
  2. Look at row counts before and after each step. Which node lets too many rows through?
  3. Check access method. Is this table being scanned, searched, sorted, or repeatedly probed?
  4. Map nodes back to SQL clauses. Which WHERE, JOIN, GROUP BY, or ORDER BY introduced that work?
  5. Change one thing at a time. Add an index, rewrite one predicate, or reduce selected columns, then compare the new plan.

That last point matters. Execution plans are only useful when you can compare "before" and "after" with a disciplined workflow.

Tool Workflow

Use tools when the plan tells you something is wrong but not why

Execution plans reveal where the database spends effort. These tools help you connect the plan back to the SQL shape and the anti-patterns causing it.

SQL Playground

Run reduced examples, add sample indexes, and compare before-and-after EXPLAIN output in a safe browser sandbox.

SQL Query Analyzer

Run a fast static check for SELECT *, leading wildcards, NOT IN traps, and other patterns that often explain expensive plan nodes.

SQL Query Explainer

Break the query into clauses so it is easier to map each part of the SQL to the nodes you see in EXPLAIN output.

Related Articles

  • Understanding Database Indexes for the scan-versus-seek decisions that show up in almost every plan.
  • SQL Optimization Techniques for a broader checklist covering pagination, batching, joins, and covering indexes.
  • SQL Anti-Patterns: The Silent Performance Killers in Your Queries for the query shapes that create bad plans even when indexes exist.

Conclusion

Reading execution plans may look like deciphering ancient hieroglyphs at first, but it's the most reliable way to solve existing performance problems. Next time your query hangs, don't guess—EXPLAIN it!

Share this article:

Topic Path

This article belongs to a larger cluster

If this page matches the problem you are working on, jump to the topic hub to see the surrounding articles in the same path instead of treating this as a one-off post.

Performance

Query performance and optimization

Start here if your goal is faster SQL. This path connects indexing, execution plans, anti-patterns, and production query tuning.

Open topic hub

Related Articles

sqlperformance

Essential SQL Optimization Techniques for Faster Queries

Is your query taking forever? Learn proven optimization techniques: indexing strategies, JOIN optimization, subquery rewrites, and execution plan analysis.

Read more
sqlperformance

SQL Anti-Patterns: The Silent Performance Killers in Your Queries

Is your query working but slow? You might be using an Anti-Pattern. Learn about SARGability, Implicit Conversions, and why your WHERE clauses are bypassing your indexes.

Read more
sqlperformance

Understanding Database Indexes: The Key to Performance

Why is my query slow? The answer is usually indexes. Learn how database indexes work, when to create them, and how they speed up your SQL.

Read more
Previous

Understanding SQL Views: Your Virtual Tables Explained

Next

Mastering Recursive CTEs: The Inception of SQL

Comments

© 2026 SQL Boy. Built for developers.

ContactPrivacyTermsAI Usage PolicyEditorial StandardsAboutRSS Feed