Correlated subqueries execution model in PostgreSQL - Time & Space Complexity
When using correlated subqueries, the database runs a small query repeatedly for each row of the main query.
We want to understand how the total work grows as the main table gets bigger.
Analyze the time complexity of the following code snippet.
SELECT e.employee_id, e.name
FROM employees e
WHERE e.salary > (
SELECT AVG(salary)
FROM employees
WHERE department_id = e.department_id
);
This query finds employees whose salary is above the average salary in their department using a correlated subquery.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: For each employee row, the subquery calculates the average salary for that employee's department.
- How many times: The subquery runs once per employee in the main query.
As the number of employees grows, the subquery runs more times, once per employee.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 subquery executions |
| 100 | About 100 subquery executions |
| 1000 | About 1000 subquery executions |
Pattern observation: The total work grows roughly in direct proportion to the number of employees.
Time Complexity: O(n * m)
This means the total work grows linearly with the number of rows in the main table times the average number of employees per department.
[X] Wrong: "The subquery runs only once, so the query is very fast regardless of table size."
[OK] Correct: Because the subquery depends on each row of the main query, it runs repeatedly, increasing total work as the table grows.
Understanding how correlated subqueries scale helps you write efficient queries and explain performance in real projects.
"What if we replaced the correlated subquery with a join and group by? How would the time complexity change?"
Practice
correlated subquery in PostgreSQL?Solution
Step 1: Understand the definition of correlated subquery
A correlated subquery references columns from the outer query and runs once per outer row.Step 2: Compare options with this behavior
Only It runs the inner query once for each row of the outer query. correctly states that the inner query runs once per outer row.Final Answer:
It runs the inner query once for each row of the outer query. -> Option AQuick Check:
Correlated subquery = runs per outer row [OK]
- Thinking inner query runs once for all rows
- Assuming no reference to outer query columns
- Confusing correlated with uncorrelated subqueries
Solution
Step 1: Identify the correlated subquery condition
SELECT e.name FROM employees e WHERE e.salary > (SELECT AVG(salary) FROM employees e2 WHERE e2.department = e.department); references the outer query aliase.departmentinside the inner query, making it correlated.Step 2: Check other options for correlation
Options B, C, and D do not reference outer query columns inside the subquery, so they are not correlated.Final Answer:
SELECT e.name FROM employees e WHERE e.salary > (SELECT AVG(salary) FROM employees e2 WHERE e2.department = e.department); -> Option AQuick Check:
Correlation requires outer reference inside subquery [OK]
- Missing outer query reference in subquery
- Using same alias for outer and inner queries
- Confusing filtering with correlation
departments(id, name) and employees(id, name, department_id, salary), what will this query return?SELECT d.name, (SELECT AVG(e.salary) FROM employees e WHERE e.department_id = d.id) AS avg_salary FROM departments d;
Solution
Step 1: Identify the correlation in the subquery
The subquery usese.department_id = d.id, linking inner query to outer query row.Step 2: Understand the output
For each department, the subquery calculates average salary of employees in that department.Final Answer:
Each department name with the average salary of its employees. -> Option BQuick Check:
Correlated subquery returns per outer row result [OK]
- Assuming subquery returns overall average salary
- Thinking subquery is uncorrelated and runs once
- Confusing department and employee columns
SELECT e1.name FROM employees e1 WHERE e1.salary > (SELECT AVG(salary) FROM employees e1 WHERE e1.department_id = e1.department_id);
Solution
Step 1: Analyze alias usage in inner and outer queries
Both inner and outer queries use aliase1, causing the inner query to reference itself only.Step 2: Understand impact on correlation
Because of alias shadowing, the subquery conditione1.department_id = e1.department_idis always true and not correlated to outer row.Final Answer:
The inner query alias shadows the outer query alias causing incorrect correlation. -> Option DQuick Check:
Use different aliases to avoid shadowing [OK]
- Using same alias for inner and outer queries
- Assuming WHERE clause is missing
- Confusing alias shadowing with syntax error
Solution
Step 1: Understand the requirement
We need to compare the max salary per department with the average salary per department.Step 2: Analyze each option's logic
SELECT d.name FROM departments d WHERE (SELECT MAX(e.salary) FROM employees e WHERE e.department_id = d.id) > (SELECT AVG(e2.salary) FROM employees e2 WHERE e2.department_id = d.id); uses two correlated subqueries referencingd.idto get max and average salary per department, correctly implementing the condition.Step 3: Identify errors in other options
SELECT d.name FROM departments d WHERE MAX(e.salary) > AVG(e.salary) FROM employees e WHERE e.department_id = d.id; has invalid syntax mixing aggregation and WHERE clause. SELECT d.name FROM departments d JOIN employees e ON e.department_id = d.id WHERE e.salary > (SELECT AVG(salary) FROM employees); compares employee salary to overall average, not per department. SELECT d.name FROM departments d WHERE EXISTS (SELECT 1 FROM employees e WHERE e.department_id = d.id AND e.salary > (SELECT AVG(salary) FROM employees)); compares employee salary to overall average, not per department.Final Answer:
SELECT d.name FROM departments d WHERE (SELECT MAX(e.salary) FROM employees e WHERE e.department_id = d.id) > (SELECT AVG(e2.salary) FROM employees e2 WHERE e2.department_id = d.id); -> Option CQuick Check:
Use correlated subqueries for per-department aggregates [OK]
- Mixing aggregation without GROUP BY
- Comparing to overall average instead of per department
- Incorrect syntax for aggregation in WHERE clause
Ready to prove it?
Pass the test and this pattern moves unknown
