Correlated subqueries let you compare each row of one table with rows in another table. They help answer questions where you need to check related data row by row.
Correlated subqueries execution model in PostgreSQL
SELECT column1, column2 FROM table1 WHERE columnX operator ( SELECT columnY FROM table2 WHERE table2.columnZ = table1.columnA );
The subquery refers to a column from the outer query, making it 'correlated'.
The subquery runs once for each row of the outer query, which can affect performance.
SELECT e.name FROM employees e WHERE e.salary > ( SELECT AVG(salary) FROM employees WHERE department = e.department );
SELECT c.customer_id FROM customers c WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id AND o.amount > 100 );
This query lists employees who earn more than the average salary in their department. The subquery calculates the average salary for the department of each employee row.
CREATE TABLE employees ( id SERIAL PRIMARY KEY, name TEXT, department TEXT, salary INT ); INSERT INTO employees (name, department, salary) VALUES ('Alice', 'Sales', 5000), ('Bob', 'Sales', 4000), ('Carol', 'HR', 4500), ('Dave', 'HR', 5500); SELECT name, salary, department FROM employees e WHERE salary > ( SELECT AVG(salary) FROM employees WHERE department = e.department );
Correlated subqueries can be slower because the inner query runs for each outer row.
Sometimes you can rewrite correlated subqueries using JOINs for better performance.
Understanding how the subquery depends on the outer query helps avoid mistakes.
Correlated subqueries run the inner query once per outer row, using values from that row.
They are useful for row-by-row comparisons or checks involving related tables.
Be mindful of performance and consider alternatives like JOINs when possible.
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
