Query Analysis Using EXPLAIN in MySQL
Course: MySQL Training
Topic: Query Analysis Using EXPLAIN
Course Link: MySQL Training Course
Demo Link: Register for a Course Demo
1. Introduction to Query Analysis Using EXPLAIN
Query analysis is the process of examining how MySQL executes an SQL query and identifying opportunities to improve its performance. As databases grow, queries that once ran quickly may become slow because of large tables, missing indexes, inefficient joins, unnecessary sorting, or poorly written conditions.
MySQL provides the EXPLAIN statement to show the execution plan for a query. The execution plan describes how MySQL intends to retrieve data, which tables it will access, which indexes it may use, how rows are accessed, and the order in which tables are processed.
Understanding EXPLAIN helps developers and database administrators diagnose performance problems before changing queries or database structures.
Why Is Query Analysis Important?
- Identifies queries that may scan large portions of a table.
- Helps determine whether appropriate indexes are being used.
- Shows how MySQL processes joins between tables.
- Helps investigate sorting and grouping operations.
- Supports evidence-based query optimization.
- Reduces unnecessary database work and resource consumption.
- Improves application response time when optimization is successful.
2. Learning Objectives
After completing this topic, learners should be able to:
- Explain the purpose of query execution plans.
- Use basic and extended forms of
EXPLAIN.
- Understand important columns in traditional EXPLAIN output.
- Recognize common table access methods.
- Identify possible index and join performance problems.
- Understand the difference between estimated and actual execution information.
- Apply practical techniques to investigate slow SQL queries.
3. What Is EXPLAIN in MySQL?
EXPLAIN is a MySQL statement that displays information about the execution plan for a supported SQL statement. It helps you understand how the optimizer plans to retrieve or modify data.
For a SELECT query, the execution plan can reveal the access method, candidate indexes, selected index, estimated number of rows examined, filtering estimates, and other details.
Basic Syntax
EXPLAIN
SELECT column1, column2
FROM table_name
WHERE condition;
Example
EXPLAIN
SELECT *
FROM employees
WHERE department_id = 10;
This statement asks MySQL to display the execution plan for the query. It does not normally execute the SELECT query to retrieve its result set; instead, it provides information about the planned execution.
Important Note
The plan is an optimizer estimate, not a guarantee of the exact work that will occur at runtime. Actual behavior can vary with table statistics, data distribution, indexes, server configuration, and parameter values.
4. Understanding a Sample Database
Consider the following example table for employee records.
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
employee_name VARCHAR(100),
department_id INT,
salary DECIMAL(10,2),
city VARCHAR(50)
);
Insert a few sample records:
INSERT INTO employees
(employee_id, employee_name, department_id, salary, city)
VALUES
(1, 'Aarav', 10, 45000, 'Mumbai'),
(2, 'Diya', 20, 55000, 'Pune'),
(3, 'Kabir', 10, 60000, 'Mumbai'),
(4, 'Meera', 30, 48000, 'Delhi'),
(5, 'Rohan', 20, 70000, 'Pune');
Now analyze this query:
EXPLAIN
SELECT employee_name, salary
FROM employees
WHERE department_id = 10;
MySQL evaluates possible access methods and chooses a plan based on its estimates and available indexes. The exact output depends on the MySQL version, table statistics, and indexes defined on the table.
5. Traditional EXPLAIN Output
In the traditional tabular format, EXPLAIN commonly returns columns such as the following. Some columns or values may vary by MySQL version and query type.
| Column |
Meaning |
id |
Identifies a SELECT query block within the execution plan. |
select_type |
Describes the type of SELECT block, such as SIMPLE or PRIMARY. |
table |
Indicates the table or derived result being accessed. |
partitions |
Shows the partitions considered for the query, when applicable. |
type |
Describes the access method MySQL uses for the table. |
possible_keys |
Lists indexes that the optimizer considers potentially useful. |
key |
Shows the index selected by the optimizer, if any. |
key_len |
Shows the length of the selected index portion used by the plan. |
ref |
Shows columns or constants compared with the selected index. |
rows |
Estimates how many rows MySQL may need to examine for that table access. |
filtered |
Estimates the percentage of examined rows expected to pass a condition. |
Extra |
Provides additional execution-plan details. |
6. Understanding the Type Column
The type column describes how MySQL accesses a table. It is an important clue when reviewing a plan, but it must be interpreted in context. A full table scan can be reasonable for a small table or a query that needs most of its rows.
| Access Type |
Description |
General Interpretation |
system |
A special case of a table with one row. |
Usually very inexpensive. |
const |
A table is accessed using a unique index lookup that can return at most one matching row. |
Usually efficient. |
eq_ref |
A unique index lookup is performed for each combination of rows from preceding tables. |
Often efficient for joins. |
ref |
An index lookup returns rows matching a value that is not necessarily unique. |
Often useful. |
range |
An index is used to retrieve a range of values. |
Often useful for ranges and inequalities. |
index |
MySQL scans an index rather than performing a full table scan. |
Can still be expensive if many index entries are scanned. |
ALL |
MySQL performs a full table scan. |
Investigate for large tables, but do not assume it is always wrong. |
Example of a Full Table Scan
EXPLAIN
SELECT *
FROM employees
WHERE city = 'Mumbai';
If no useful index exists on city, MySQL may choose ALL and inspect the table's rows. Whether this is a problem depends on the table size, selectivity of the condition, and the query's overall cost.
7. Understanding possible_keys and key
The possible_keys column lists indexes that might help the query. The key column identifies the index that MySQL actually selects for that plan.
- possible_keys is NULL: MySQL has not identified a suitable candidate index for that table access. The query may not need an index, or a useful index may be missing.
- key is NULL: MySQL chooses not to use an index for that access. A table scan may be cheaper, especially for small tables or queries returning many rows.
- key contains an index name: MySQL plans to use that index for the access method shown.
Creating an Index
CREATE INDEX idx_employees_department
ON employees(department_id);
Then examine the query again:
EXPLAIN
SELECT employee_name, salary
FROM employees
WHERE department_id = 10;
MySQL may choose the new index if its optimizer estimates that using it is less expensive. On a tiny sample table, it may still choose a scan because scanning a few rows can be cheaper than accessing an index and then retrieving table records.
8. Understanding rows and filtered
8.1 rows
The rows column is an estimate of the number of rows MySQL expects to examine for the table access. It is not necessarily the number of rows returned to the user.
8.2 filtered
The filtered column estimates the percentage of examined rows that remain after additional filtering conditions are applied. It is an estimate, not a guaranteed measured percentage.
For example, if EXPLAIN estimates that 1,000 rows will be examined and 10% will pass a filter, the plan may estimate that approximately 100 rows remain after that filtering stage. This is only an illustration of how the estimates relate.
Why These Columns Matter
- A large estimated row count may indicate that the query examines more data than expected.
- A low filtered percentage may suggest that many rows are discarded after the access step.
- Estimates that differ significantly from actual execution can indicate inaccurate statistics or skewed data distribution.
9. Understanding the Extra Column
The Extra column contains additional information about the planned execution. The following are common values or descriptions you may encounter.
| Extra Value |
Meaning |
What to Check |
Using where |
A condition is used to filter rows. |
Check whether the condition is selective and whether a useful index can help. |
Using index |
The query can be satisfied from index entries without needing to read the full table rows for the selected columns. |
This can be efficient; confirm that the index supports the query well. |
Using filesort |
MySQL performs an additional sorting operation rather than satisfying the order directly through the chosen index. |
Review ORDER BY columns, indexes, and the number of rows being sorted. |
Using temporary |
A temporary table is used for part of query processing. |
Review GROUP BY, DISTINCT, ORDER BY, and the query structure. |
Using index condition |
Index Condition Pushdown is used to evaluate a condition using index entries before fetching full rows where possible. |
Check whether the index and predicate are appropriate. |
Impossible WHERE |
The optimizer determines that the WHERE condition cannot be true. |
Check for contradictory conditions or incorrect values. |
These messages are diagnostic clues, not automatic proof of a performance problem. For example, Using filesort does not necessarily mean that MySQL writes all sorted data to disk, and Using temporary is not always avoidable.
10. Analyzing WHERE Conditions
The WHERE clause filters rows. EXPLAIN helps determine whether MySQL can use an index to find matching records efficiently.
Example: Filtering by Department
EXPLAIN
SELECT employee_id, employee_name
FROM employees
WHERE department_id = 20;
If an appropriate index exists, MySQL may use it to locate matching employees. The chosen access method depends on the index structure, table statistics, and estimated number of matching records.
Example: Function Applied to an Indexed Column
EXPLAIN
SELECT *
FROM employees
WHERE YEAR(hire_date) = 2025;
Applying a function to a column can prevent a conventional index on that column from being used for a direct range lookup. If the goal is to retrieve records within a year, a date range is often more index-friendly.
SELECT *
FROM employees
WHERE hire_date >= '2025-01-01'
AND hire_date < '2026-01-01';
Use this example only when the table has a compatible hire_date column. A suitable index on that column may support a range access.
11. Analyzing JOIN Operations
Joins combine related rows from two or more tables. EXPLAIN helps identify join order, access methods, selected indexes, and estimated row counts for each table.
Example Tables
CREATE TABLE departments (
department_id INT PRIMARY KEY,
department_name VARCHAR(100)
);
Example JOIN Query
EXPLAIN
SELECT e.employee_name, d.department_name
FROM employees AS e
JOIN departments AS d
ON e.department_id = d.department_id
WHERE d.department_id = 10;
When analyzing the plan:
- Inspect the order in which the tables appear.
- Check the
type and key columns for each table.
- Confirm that join columns have compatible data types and appropriate indexes.
- Review estimated row counts to understand how much data may flow through the join.
- Do not assume that the SQL order of tables is always the optimizer's execution order.
12. Analyzing ORDER BY and GROUP BY
Sorting and grouping can require additional processing, particularly when large result sets are involved.
ORDER BY Example
EXPLAIN
SELECT employee_id, employee_name, salary
FROM employees
ORDER BY salary DESC;
If the query must sort many rows, EXPLAIN may show Using filesort. An index that matches the filtering and ordering requirements may help in some cases, but its usefulness depends on the query and index structure.
GROUP BY Example
EXPLAIN
SELECT department_id, COUNT(*) AS employee_count
FROM employees
GROUP BY department_id;
Check for temporary-table or sorting indicators and review how much data is being processed. Do not add indexes without checking the actual query pattern and the trade-offs for inserts, updates, storage, and maintenance.
13. EXPLAIN FORMAT=JSON
MySQL supports multiple execution-plan formats. The JSON format presents plan information as a structured document, which is useful for investigating more detailed optimizer information.
Syntax
EXPLAIN FORMAT=JSON
SELECT employee_name, salary
FROM employees
WHERE department_id = 10;
JSON output may contain details such as table names, access types, selected indexes, estimated rows, filtering estimates, attached conditions, and cost information.
When to Use JSON Format
- When traditional output does not provide enough detail.
- When examining optimizer estimates and query cost information.
- When investigating nested queries or complex joins.
- When comparing plan details across different query versions.
14. EXPLAIN FORMAT=TREE
The TREE format represents the execution plan as a hierarchy of operations. It can make the order of operations easier to understand for complex queries. It also displays details such as hash joins that are not shown in the traditional format.
EXPLAIN FORMAT=TREE
SELECT employee_name, salary
FROM employees
WHERE department_id = 10;
Use TREE when you want a more descriptive view of the operations MySQL expects to perform. Availability and supported features depend on your MySQL version.
15. Understanding EXPLAIN ANALYZE
EXPLAIN ANALYZE executes a supported statement and reports the execution plan along with runtime measurements. It helps compare the optimizer's estimates with observed execution behavior.
Syntax
EXPLAIN ANALYZE
SELECT employee_name, salary
FROM employees
WHERE department_id = 10;
In supported MySQL versions, EXPLAIN ANALYZE uses TREE output and reports actual execution details for plan operations.
| Information |
Meaning |
| Estimated cost |
The optimizer's estimated cost for an operation. |
| Estimated rows |
The expected number of rows produced by an operation. |
| Actual time |
Measured time associated with execution of the operation. |
| Actual rows |
The number of rows actually produced by the operation. |
| Loops |
The number of times the operation ran. |
Important: Unlike a regular EXPLAIN of a SELECT query, EXPLAIN ANALYZE actually runs the query. Use it thoughtfully with expensive queries or production workloads, because it can consume resources and take time.
16. Comparing Estimated and Actual Execution
One of the most useful applications of EXPLAIN ANALYZE is to identify where optimizer estimates differ from observed results.
For example, MySQL might estimate that an operation returns 20 rows but the operation actually returns 5,000 rows. A large difference can affect join choices and the overall execution strategy.
What to Investigate
- Whether table statistics accurately represent the data.
- Whether data values are unevenly distributed across the table.
- Whether filter conditions are more or less selective than estimated.
- Whether a join produces many more rows than expected.
- Whether repeated loops cause an operation to perform more work than anticipated.
Large estimate differences are clues for further investigation, not proof that the optimizer is incorrect. Always evaluate them alongside the query, schema, data distribution, and observed performance.
17. Query Optimization Workflow Using EXPLAIN
A consistent workflow makes query tuning easier to measure and repeat.
- Identify the slow query: Use application monitoring, database logs, or query profiling tools to find a query that needs attention.
- Capture the original query: Record the SQL statement, relevant parameters, approximate data volume, and the performance problem.
- Run EXPLAIN: Inspect the execution plan before making changes.
- Review access methods: Check the
type, possible_keys, and key columns.
- Review row estimates: Investigate unexpectedly high row counts or filtering estimates.
- Inspect joins and sorting: Look at join order, join indexes, temporary-table indicators, and sorting operations.
- Choose one improvement: Consider an index, a revised predicate, a narrower result set, or a simpler query structure.
- Re-run EXPLAIN: Check whether the plan changes as expected.
- Measure performance: Compare execution time and resource usage under representative conditions.
- Document the result: Keep the change only if testing shows a meaningful benefit without unacceptable trade-offs.
18. Common Query Performance Problems and Solutions
| Problem |
Possible Cause |
Recommended Investigation |
| Slow query on a large table |
Too many rows are scanned. |
Inspect the access method, filters, and available indexes. |
| Index is not selected |
The table is small, many rows match, or the index is not useful for the predicate. |
Review the plan, query selectivity, and table statistics before changing indexes. |
| Slow JOIN |
Join conditions or indexes are inefficient. |
Check join columns, data types, row estimates, and the chosen access method. |
| Slow ORDER BY |
Many rows require sorting. |
Review the filter and ordering columns; test whether an appropriate index helps. |
| Unexpected row estimates |
Statistics may be stale or the data distribution may be uneven. |
Consider updating table statistics and comparing estimated versus actual execution. |
| Large temporary operation |
Grouping, sorting, or other processing requires intermediate results. |
Review the query shape and whether the intermediate work is necessary. |
| Query returns too much data |
Unnecessary columns or rows are requested. |
Select only required columns and apply appropriate filtering. |
19. Using ANALYZE TABLE
MySQL's optimizer relies on table and index statistics to estimate query costs. When statistics do not represent the current data well, the optimizer may choose a less suitable plan.
The ANALYZE TABLE statement updates key distribution statistics for supported storage engines.
ANALYZE TABLE employees;
After updating statistics, inspect the plan again:
EXPLAIN
SELECT employee_name, salary
FROM employees
WHERE department_id = 10;
Running ANALYZE TABLE does not guarantee that a query will become faster. Confirm the result using the execution plan and representative performance tests.
20. Best Practices for Query Analysis
- Use EXPLAIN before making assumptions about how a query works.
- Focus on the amount of work performed, not just one column in the output.
- Do not treat every
ALL access type as a problem; consider table size and query requirements.
- Do not create indexes solely because an EXPLAIN output shows
key = NULL.
- Use composite indexes when justified by real query patterns and predicate order.
- Avoid selecting every column when only a few are required.
- Check join conditions for compatible data types and suitable indexes.
- Use EXPLAIN ANALYZE carefully because it executes the query.
- Compare the plan before and after each meaningful change.
- Test with realistic data volumes and representative parameters.
- Consider the write overhead, storage cost, and maintenance needs of additional indexes.
- Keep records of query changes and their measured impact.
21. Practical Exercises
Exercise 1: Analyze a Simple Filter
EXPLAIN
SELECT *
FROM employees
WHERE department_id = 20;
Tasks:
- Identify the access type.
- Check whether an index is selected.
- Record the estimated row count.
- Explain whether the plan appears reasonable for the table's size.
Exercise 2: Analyze a Sorting Query
EXPLAIN
SELECT employee_id, employee_name, salary
FROM employees
ORDER BY salary DESC;
Tasks:
- Look for sorting-related information in Extra.
- Consider whether an index might help.
- Explain how the result could differ if the table contained millions of rows.
Exercise 3: Analyze a JOIN
EXPLAIN
SELECT e.employee_name, d.department_name
FROM employees AS e
JOIN departments AS d
ON e.department_id = d.department_id;
Tasks:
- Identify the table access order.
- Review the access method for each table.
- Identify the indexes used for the join, if any.
- Suggest one possible improvement and explain how you would test it.
Exercise 4: Compare Estimated and Actual Execution
EXPLAIN ANALYZE
SELECT employee_name
FROM employees
WHERE department_id = 10;
Tasks:
- Compare estimated rows with actual rows.
- Review the actual time and loop count.
- Explain any significant differences.
- Record why a small sample table may not reveal production-scale performance issues.
22. Mini Project: Analyze and Improve Employee Queries
Project Goal: Practice analyzing query plans and validating improvements using a sample employee database.
Project Requirements
- Create an employee table with employee ID, name, department, salary, and city columns.
- Populate it with enough representative records to make comparisons meaningful.
- Choose three queries that filter, join, or sort employee data.
- Run EXPLAIN for each query and record the original plan.
- Identify possible inefficiencies without assuming that any single plan indicator proves a problem.
- Make one carefully selected change, such as adding an index or rewriting a filter.
- Run EXPLAIN again and compare the plans.
- Where supported and appropriate, use EXPLAIN ANALYZE to inspect actual execution.
- Measure performance consistently and document the trade-offs.
Expected Deliverables
- SQL scripts for the sample schema and test queries.
- Original and revised execution plans.
- A short explanation of the changes made.
- Performance observations under documented test conditions.
- A final summary of the improvements and remaining limitations.
23. Interview Questions and Answers
Q1. What is EXPLAIN in MySQL?
Answer: EXPLAIN displays optimizer information about how MySQL plans to execute a supported SQL statement, including table access methods, indexes, row estimates, and other plan details.
Q2. Does a regular EXPLAIN execute a SELECT query?
Answer: A regular EXPLAIN of a SELECT normally displays its execution plan without retrieving the SELECT result set. EXPLAIN ANALYZE, by contrast, executes the supported query to collect runtime information.
Q3. What does type = ALL mean?
Answer: It indicates a full table scan for that table access. This can be expensive on a large table, but it can be reasonable when a table is small or most rows are needed.
Q4. What is the difference between possible_keys and key?
Answer: possible_keys lists candidate indexes the optimizer considers, while key identifies the index selected for the plan, if one is used.
Q5. What does the rows column represent?
Answer: It estimates the number of rows MySQL expects to examine for the relevant table access. It is not necessarily the number of rows returned to the user.
Q6. What does Using filesort indicate?
Answer: It indicates that MySQL performs an additional sorting operation instead of obtaining the required ordering directly from the chosen index. It does not automatically mean disk sorting or a serious performance problem.
Q7. What is EXPLAIN FORMAT=JSON?
Answer: It presents execution-plan information as structured JSON, which can expose detailed optimizer estimates and access information.
Q8. What is EXPLAIN ANALYZE?
Answer: It executes a supported query and provides runtime measurements alongside the execution plan, allowing estimated behavior to be compared with actual execution.
Q9. Why might MySQL ignore an available index?
Answer: The optimizer may estimate that a table scan is cheaper, the condition may match many rows, the index may not support the query effectively, or statistics may influence the decision.
Q10. How can inaccurate estimates be investigated?
Answer: Compare estimated and actual row counts where available, examine data distribution, check table statistics, and use ANALYZE TABLE when appropriate. Then test whether the plan and performance improve.
Q11. Does adding an index always improve performance?
Answer: No. Indexes consume storage and add work to inserts, updates, and deletes. They should be designed for actual query patterns and validated through testing.
Q12. What is the main goal of query analysis?
Answer: The goal is to understand where database work occurs, identify plausible performance bottlenecks, and validate changes that improve performance without creating unacceptable trade-offs.
24. Quick Revision Checklist
- Understand the purpose of EXPLAIN.
- Know how to read
id, select_type, table, and type.
- Understand
possible_keys, key, key_len, and ref.
- Interpret
rows and filtered as estimates.
- Recognize common Extra indicators, including
Using where, Using index, Using filesort, and Using temporary.
- Know when to use JSON and TREE output.
- Understand that EXPLAIN ANALYZE runs the query.
- Compare estimates with actual measurements where available.
- Validate proposed indexes and query changes with realistic tests.
- Document the plan and performance before and after optimization.
25. Conclusion
Query Analysis Using EXPLAIN is an essential MySQL skill for understanding how SQL statements access tables, use indexes, process joins, filter records, and perform sorting or grouping. By interpreting execution-plan information, developers can identify areas that deserve further investigation and make informed optimization decisions.
For the most reliable results, use EXPLAIN to inspect the plan, EXPLAIN ANALYZE when actual runtime information is needed and safe to collect, and repeatable performance tests to confirm whether a change truly helps. Effective query optimization is based on evidence, realistic data, and a clear understanding of trade-offs.
26. Additional Learning Resources