Correlated Subqueries
How Correlated Subqueries Conceptually Execute
Because the inner query depends on a value from the current outer row, you can think of it as re-running once per outer row, each time substituting that row's value in. A real database engine may optimize this internally, but conceptually, a correlated subquery answers a slightly different question for every single row of the outer table.
-- Employees who earn more than the average salary in THEIR OWN department SELECT e.employee_name, e.department, e.salary FROM employees e WHERE e.salary > ( SELECT AVG(e2.salary) FROM employees e2 WHERE e2.department = e.department -- references the OUTER row );
WHERE e2.department = e.department line ties the inner AVG to whichever department the current outer row (e) belongs to. So an employee in Engineering is compared against the Engineering average, and an employee in Sales is compared against the Sales average, all within a single statement.The Equivalent Rewritten as a JOIN
-- Same result, expressed with a JOIN against a pre-aggregated derived table SELECT e.employee_name, e.department, e.salary FROM employees e JOIN ( SELECT department, AVG(salary) AS avg_salary FROM employees GROUP BY department ) AS dept_avg ON dept_avg.department = e.department WHERE e.salary > dept_avg.avg_salary;
Approach | Readability | Typical Performance |
|---|---|---|
Correlated subquery | Very close to the plain-English question | Can be slow on large tables if not optimized well |
JOIN with derived table | Slightly more setup, but scales predictably | Aggregation happens once, then a single join |
Correlated Subqueries in SELECT
Correlated subqueries are not limited to WHERE — they are just as common in the SELECT list, computing a per-row value that depends on something about that specific row.
-- For each customer, show how many orders they have placed
SELECT
c.customer_id,
c.name,
(
SELECT COUNT(*)
FROM orders o
WHERE o.customer_id = c.customer_id
) AS order_count
FROM customers c;A correlated subquery references a column from the outer query, so it cannot run independently.
Conceptually, it re-evaluates once per outer row — a real performance consideration on large tables.
Query optimizers can sometimes rewrite correlated subqueries into JOINs automatically, but not always.
When performance matters, compare EXPLAIN output for the correlated subquery version against an equivalent JOIN.