VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
mysql -u root -p sakila before you start.
How a correlated subquery works
COLUMN_LIST with the columns you want, TABLE_NAME with the table,
COLUMN_NAME with the columns being compared, AGGREGATE_FUNCTION with the
function the inner query runs, and OUTER_ALIAS and INNER_ALIAS with 2 names
for the tables involved.
The 2 aliases are what make it work: the inner query mentions the outer one by
name, which is the correlation.
Read it as a question asked once per outer row, with that row’s values filled
in.
The question it answers
“Which films are longer than average” needs 1 average. “Which films are longer than average for their rating” needs a different average per row, and that is what a correlated subquery gives you.f2.rating = f.rating is the line that matters. It ties the inner query to the
row being tested, so a G film is compared against the G average and an R
film against the R average.
Take that line out and the subquery averages the whole catalog, the same
value every time, and the query answers a different question.
It runs once per row
Compare the timing above with the queries in the Subqueries lesson, which return too fast to measure. This one takes a tenth of a second on 1,000 rows, and your exact figure will differ, because the server evaluates the inner query per outer row rather than once. That is the whole trade. A correlated subquery is the clearest way to write “compare each row against its own group”, and the cost grows with the table. MySQL rewrites some correlated subqueries into joins by itself, and there are other shapes that answer the same question in 1 pass. Subqueries vs JOINs in MySQL covers both, and reading the plan that tells you which you got.Summary
- Use a correlated subquery to compare each row against something computed from its own group.
- Give both tables aliases, because the inner query has to name the outer one.
- Expect it to run once per outer row.
- Rewrite it as a derived table and a join when the table is large.
See also
- Subqueries — the uncorrelated kind, which runs once
- EXISTS — a correlated subquery used as a test rather than a value
- Derived tables — the cheaper rewrite
- Subqueries vs JOINs in MySQL — reading the plan to decide

