Summary
Query scans 2 tables and sorts the result; examines ~331,614 rows to return 9 in 6357 ms. Main finding: full scan of dept_emp (331,603 rows).
- Total time
- 6.36 s
- Rows returned
- 9
- Rows examined
- 331.61K
- Operators
- 10
Primary recommendation
Add indexes on filter/join columns to avoid full table scans
Why does this help?
An index turns a full-table scan into a direct lookup — MySQL jumps straight to the matching rows instead of reading every row and discarding the ones that don't match. For joins, the benefit compounds: a nested loop over N outer rows that had to scan the inner table becomes N fast lookups.
SQL
select d.dept_name AS dept_name,round(AVG(s.salary),2) AS avg_salary
from employees.salaries s
join employees.dept_emp de
join employees.departments d
where ((s.emp_no = de.emp_no) and (d.dept_no = de.dept_no) and (de.to_date = DATE'9999-01-01') and (s.to_date = DATE'9999-01-01'))
group by employees.d.dept_name
order by avg_salary desc
Execution plan (bargraph)
Click a plan operator to inspect details.
Warnings (2)
-
WARN
Full table scan: <temporary> (9 rows), dept_emp (331603 rows)
Labeled:
Table scan [<temporary>]Table scan [dept_emp] -
WARN
1 sort operationLearn →(s) — 9 rows; may use disk-based filesortLearn →
Labeled:
Sort
Suggestions (2)
-
HIGH
Add indexes on filter/join columns to avoid full table scans
-
MEDIUM
Increase sort_buffer_size or add an ordered index to avoid filesortLearn →
myteach — interactive algorithm catalog
Every operator shown above has a hands-on lesson with a cost-model slider and an animated walk-through. The catalog is the central index — bookmark it once, revisit per query:
Lessons relevant to this plan
Raw sidecar (JSON)
{
"$schema": "https://myflames.dev/schemas/sidecar-v1.json",
"schema_version": "1.3",
"generated_at": "2026-04-25T12:51:25Z",
"myflames_version": "1.5.0",
"source": {
"type": "file"
},
"plan_summary": {
"total_time_ms": 6357.23,
"rows_sent": 9,
"rows_examined_estimate": 331614,
"operator_count": 10,
"max_depth": 7
},
"optimizer_switches": [],
"warnings": [
{
"severity": "warn",
"category": "full_scan",
"text": "Full table scan: <temporary> (9 rows), dept_emp (331603 rows)",
"source": "plan",
"node_labels": [
"Table scan [<temporary>]",
"Table scan [dept_emp]"
]
},
{
"severity": "warn",
"category": "filesort",
"text": "1 sort operation(s) — 9 rows; may use disk-based filesort",
"source": "plan",
"node_labels": [
"Sort"
]
}
],
"suggestions": [
{
"severity": "high",
"category": "index",
"action": "Add indexes on filter/join columns to avoid full table scans",
"source": "plan"
},
{
"severity": "medium",
"category": "tuning_variable",
"action": "Increase sort_buffer_size or add an ordered index to avoid filesort",
"source": "plan",
"target_variable": "sort_buffer_size"
}
],
"executive_summary": "Query scans 2 tables and sorts the result; examines ~331,614 rows to return 9 in 6357 ms. Main finding: full scan of dept_emp (331,603 rows).",
"plan_tree": {
"node_id": "n:4b310806699e",
"short_label": "Sort",
"folded_label": "SORT starts=1 rows=9",
"children": [
{
"node_id": "n:d2dccead858b",
"short_label": "Table scan [<temporary>]",
"folded_label": "TABLE SCAN [<temporary>] starts=1 rows=9",
"children": [
{
"node_id": "n:36c719443bc0",
"short_label": "Aggregate",
"folded_label": "AGGREGATE starts=1 rows=9",
"children": [
{
"node_id": "n:62bcf2b0e81f",
"short_label": "Nested loop inner join",
"folded_label": "NESTED LOOP INNER starts=1 rows=240124",
"children": [
{
"node_id": "n:3f3b42eaef83",
"short_label": "Nested loop inner join",
"folded_label": "NESTED LOOP INNER starts=1 rows=240124",
"children": [
{
"node_id": "n:9e86647097af",
"short_label": "Filter: ((de.to_date = DATE'9999-01-01'))",
"folded_label": "FILTER ((de.to_date = DATE'9999-01-01')) starts=1 rows=240124",
"children": [
{
"node_id": "n:f594e5f3a9f7",
"short_label": "Table scan [dept_emp]",
"folded_label": "TABLE SCAN [dept_emp] starts=1 rows=331603",
"children": []
}
]
},
{
"node_id": "n:d0f7a733b6e0",
"short_label": "Filter: ((s.to_date = DATE'9999-01-01'))",
"folded_label": "FILTER ((s.to_date = DATE'9999-01-01')) starts=240124 rows=1",
"children": [
{
"node_id": "n:9d8125b7e0a1",
"short_label": "Index lookup [salaries.PRIMARY]",
"folded_label": "INDEX LOOKUP [salaries.PRIMARY] starts=240124 rows=11",
"children": []
}
]
}
]
},
{
"node_id": "n:661c6ab5d2c3",
"short_label": "Single-row lookup [departments.PRIMARY]",
"folded_label": "SINGLE ROW LOOKUP [departments.PRIMARY] starts=240124 rows=1",
"children": []
}
]
}
]
}
]
}
]
},
"query": {
"raw": "/* select#1 */ select `d`.`dept_name` AS `dept_name`,round(AVG(s.salary),2) AS `avg_salary` from `employees`.`salaries` `s` join `employees`.`dept_emp` `de` join `employees`.`departments` `d` where ((`s`.`emp_no` = `de`.`emp_no`) and (`d`.`dept_no` = `de`.`dept_no`) and (`de`.`to_date` = DATE'9999-01-01') and (`s`.`to_date` = DATE'9999-01-01')) group by `employees`.`d`.`dept_name` order by `avg_salary` desc",
"beautified": "select d.dept_name AS dept_name,round(AVG(s.salary),2) AS avg_salary\nfrom employees.salaries s\n join employees.dept_emp de\n join employees.departments d\nwhere ((s.emp_no = de.emp_no) and (d.dept_no = de.dept_no) and (de.to_date = DATE'9999-01-01') and (s.to_date = DATE'9999-01-01'))\ngroup by employees.d.dept_name\norder by avg_salary desc"
},
"primary_action": {
"ref": "suggestions[0]"
},
"teach_hooks": [
{
"lesson": "filesort",
"match": {
"folded_label": "SORT starts=1 rows=9",
"short_label": "Sort"
},
"controls": {
"rows": 9,
"row_size": 200
},
"note": "Sort: avg_salary DESC",
"query_sql": "/* select#1 */ select `d`.`dept_name` AS `dept_name`,round(AVG(s.salary),2) AS `avg_salary` from `employees`.`salaries` `s` join `employees`.`dept_emp` `de` join `employees`.`departments` `d` where ((`s`.`emp_no` = `de`.`emp_no`) and (`d`.`dept_no` = `de`.`dept_no`) and (`de`.`to_date` = DATE'9999-01-01') and (`s`.`to_date` = DATE'9999-01-01')) group by `employees`.`d`.`dept_name` order by `avg_salary` desc"
},
{
"lesson": "full_scan",
"match": {
"folded_label": "TABLE SCAN [<temporary>] starts=1 rows=9",
"short_label": "Table scan [<temporary>]"
},
"controls": {
"rows": 9,
"row_size": 200,
"selectivity": 100.0
},
"note": "Table scan on <temporary> (<temporary>)",
"query_sql": "/* select#1 */ select `d`.`dept_name` AS `dept_name`,round(AVG(s.salary),2) AS `avg_salary` from `employees`.`salaries` `s` join `employees`.`dept_emp` `de` join `employees`.`departments` `d` where ((`s`.`emp_no` = `de`.`emp_no`) and (`d`.`dept_no` = `de`.`dept_no`) and (`de`.`to_date` = DATE'9999-01-01') and (`s`.`to_date` = DATE'9999-01-01')) group by `employees`.`d`.`dept_name` order by `avg_salary` desc"
},
{
"lesson": "tmp",
"match": {
"folded_label": "AGGREGATE starts=1 rows=9",
"short_label": "Aggregate"
},
"controls": {
"rows": 9,
"row_size": 200
},
"note": "Aggregate using temporary table",
"query_sql": "/* select#1 */ select `d`.`dept_name` AS `dept_name`,round(AVG(s.salary),2) AS `avg_salary` from `employees`.`salaries` `s` join `employees`.`dept_emp` `de` join `employees`.`departments` `d` where ((`s`.`emp_no` = `de`.`emp_no`) and (`d`.`dept_no` = `de`.`dept_no`) and (`de`.`to_date` = DATE'9999-01-01') and (`s`.`to_date` = DATE'9999-01-01')) group by `employees`.`d`.`dept_name` order by `avg_salary` desc"
},
{
"lesson": "nested_loop",
"match": {
"folded_label": "NESTED LOOP INNER starts=1 rows=240124",
"short_label": "Nested loop inner join"
},
"controls": {
"outer_rows": 240124,
"inner_rows": 240124,
"row_size": 200
},
"note": "Nested loop inner join",
"query_sql": "/* select#1 */ select `d`.`dept_name` AS `dept_name`,round(AVG(s.salary),2) AS `avg_salary` from `employees`.`salaries` `s` join `employees`.`dept_emp` `de` join `employees`.`departments` `d` where ((`s`.`emp_no` = `de`.`emp_no`) and (`d`.`dept_no` = `de`.`dept_no`) and (`de`.`to_date` = DATE'9999-01-01') and (`s`.`to_date` = DATE'9999-01-01')) group by `employees`.`d`.`dept_name` order by `avg_salary` desc"
},
{
"lesson": "filter",
"match": {
"folded_label": "FILTER ((de.to_date = DATE'9999-01-01')) starts=1 rows=240124",
"short_label": "Filter: ((de.to_date = DATE'9999-01-01'))"
},
"controls": {
"input_rows": 240124,
"selectivity": 10.0
},
"note": "Filter: (de.to_date = DATE'9999-01-01')",
"query_sql": "/* select#1 */ select `d`.`dept_name` AS `dept_name`,round(AVG(s.salary),2) AS `avg_salary` from `employees`.`salaries` `s` join `employees`.`dept_emp` `de` join `employees`.`departments` `d` where ((`s`.`emp_no` = `de`.`emp_no`) and (`d`.`dept_no` = `de`.`dept_no`) and (`de`.`to_date` = DATE'9999-01-01') and (`s`.`to_date` = DATE'9999-01-01')) group by `employees`.`d`.`dept_name` order by `avg_salary` desc"
},
{
"lesson": "full_scan",
"match": {
"folded_label": "TABLE SCAN [dept_emp] starts=1 rows=331603",
"short_label": "Table scan [dept_emp]"
},
"controls": {
"rows": 331603,
"row_size": 200,
"selectivity": 100.0
},
"note": "Table scan on de (dept_emp)",
"query_sql": "/* select#1 */ select `d`.`dept_name` AS `dept_name`,round(AVG(s.salary),2) AS `avg_salary` from `employees`.`salaries` `s` join `employees`.`dept_emp` `de` join `employees`.`departments` `d` where ((`s`.`emp_no` = `de`.`emp_no`) and (`d`.`dept_no` = `de`.`dept_no`) and (`de`.`to_date` = DATE'9999-01-01') and (`s`.`to_date` = DATE'9999-01-01')) group by `employees`.`d`.`dept_name` order by `avg_salary` desc"
},
{
"lesson": "filter",
"match": {
"folded_label": "FILTER ((s.to_date = DATE'9999-01-01')) starts=240124 rows=1",
"short_label": "Filter: ((s.to_date = DATE'9999-01-01'))"
},
"controls": {
"input_rows": 240124,
"selectivity": 10.0
},
"note": "Filter: (s.to_date = DATE'9999-01-01')",
"query_sql": "/* select#1 */ select `d`.`dept_name` AS `dept_name`,round(AVG(s.salary),2) AS `avg_salary` from `employees`.`salaries` `s` join `employees`.`dept_emp` `de` join `employees`.`departments` `d` where ((`s`.`emp_no` = `de`.`emp_no`) and (`d`.`dept_no` = `de`.`dept_no`) and (`de`.`to_date` = DATE'9999-01-01') and (`s`.`to_date` = DATE'9999-01-01')) group by `employees`.`d`.`dept_name` order by `avg_salary` desc"
},
{
"lesson": "non_unique_lookup",
"match": {
"folded_label": "INDEX LOOKUP [salaries.PRIMARY] starts=240124 rows=11",
"short_label": "Index lookup [salaries.PRIMARY]"
},
"controls": {
"rows": 2524737,
"selectivity": 40.0,
"covering": false
},
"note": "Index lookup on s using PRIMARY (emp_no=de.emp_no) (salaries)",
"query_sql": "/* select#1 */ select `d`.`dept_name` AS `dept_name`,round(AVG(s.salary),2) AS `avg_salary` from `employees`.`salaries` `s` join `employees`.`dept_emp` `de` join `employees`.`departments` `d` where ((`s`.`emp_no` = `de`.`emp_no`) and (`d`.`dept_no` = `de`.`dept_no`) and (`de`.`to_date` = DATE'9999-01-01') and (`s`.`to_date` = DATE'9999-01-01')) group by `employees`.`d`.`dept_name` order by `avg_salary` desc"
},
{
"lesson": "unique_lookup",
"match": {
"folded_label": "SINGLE ROW LOOKUP [departments.PRIMARY] starts=240124 rows=1",
"short_label": "Single-row lookup [departments.PRIMARY]"
},
"controls": {
"rows": 240124,
"covering": false
},
"note": "Single-row index lookup on d using PRIMARY (dept_no=de.dept_no) (departments)",
"query_sql": "/* select#1 */ select `d`.`dept_name` AS `dept_name`,round(AVG(s.salary),2) AS `avg_salary` from `employees`.`salaries` `s` join `employees`.`dept_emp` `de` join `employees`.`departments` `d` where ((`s`.`emp_no` = `de`.`emp_no`) and (`d`.`dept_no` = `de`.`dept_no`) and (`de`.`to_date` = DATE'9999-01-01') and (`s`.`to_date` = DATE'9999-01-01')) group by `employees`.`d`.`dept_name` order by `avg_salary` desc"
}
],
"operator_complexities": [
{
"node_id": "n:4b310806699e",
"folded_label": "SORT starts=1 rows=9",
"short_label": "Sort",
"complexity": {
"big_o": "O(n log n)",
"short": "n log n",
"severity": "medium",
"rationale": "Filesort: MySQL sorts the output in the server layer. Priority-queue filesort (LIMIT) is not detected today — we report the upper bound.",
"confidence": "worst_case",
"learn_more": "filesort"
}
},
{
"node_id": "n:d2dccead858b",
"folded_label": "TABLE SCAN [<temporary>] starts=1 rows=9",
"short_label": "Table scan [<temporary>]",
"complexity": {
"big_o": "O(n)",
"short": "n",
"severity": "medium",
"rationale": "Full table scan: the storage engine returns every row; cost scales with the table size.",
"confidence": "exact",
"learn_more": "full_table_scan"
}
},
{
"node_id": "n:62bcf2b0e81f",
"folded_label": "NESTED LOOP INNER starts=1 rows=240124",
"short_label": "Nested loop inner join",
"complexity": {
"big_o": "O(n · log m)",
"short": "n · log m",
"severity": "medium",
"rationale": "Indexed nested loop: each outer row probes the inner table via an index descent.",
"confidence": "exact",
"learn_more": "nested_loop_join"
}
},
{
"node_id": "n:f594e5f3a9f7",
"folded_label": "TABLE SCAN [dept_emp] starts=1 rows=331603",
"short_label": "Table scan [dept_emp]",
"complexity": {
"big_o": "O(n)",
"short": "n",
"severity": "medium",
"rationale": "Full table scan: the storage engine returns every row; cost scales with the table size.",
"confidence": "exact",
"learn_more": "full_table_scan"
}
},
{
"node_id": "n:9d8125b7e0a1",
"folded_label": "INDEX LOOKUP [salaries.PRIMARY] starts=240124 rows=11",
"short_label": "Index lookup [salaries.PRIMARY]",
"complexity": {
"big_o": "O(log n + k)",
"short": "log n + k",
"severity": "good",
"rationale": "Index lookup (ref): one B+tree descent then a sequential walk over k matching index entries. k = matching rows for the indexed predicate.",
"confidence": "exact",
"learn_more": "index_lookup"
}
},
{
"node_id": "n:661c6ab5d2c3",
"folded_label": "SINGLE ROW LOOKUP [departments.PRIMARY] starts=240124 rows=1",
"short_label": "Single-row lookup [departments.PRIMARY]",
"complexity": {
"big_o": "O(log n)",
"short": "log n",
"severity": "good",
"rationale": "Unique-key lookup (eq_ref): one B+tree descent into the inner table per outer row — as fast as MySQL can get.",
"confidence": "exact",
"learn_more": "single_row_lookup"
}
}
]
}