SQL Notes
Learn SQL Correlated Subqueries in detail, understand row-by-row execution, how they differ from normal subqueries, and solve advanced business problems efficiently.
We covered simple subqueries and nested subqueries before. In those, the inner query ran independently—it didn't care about the outer query.
But real-world problems often need something different. You need the inner query to reference the current row being processed.
For example:
- Find employees earning more than their department's average salary.
- Find products priced higher than the average in their category.
- Find students scoring above their class average.
- Find customers spending more than their city's average.
With a regular subquery, you can't do this because the comparison changes for each row.
Enter Correlated Subqueries.
A correlated subquery is a subquery that references columns from the outer query. Because it depends on the outer query, it runs once for every row.
Why "Correlated"?
The term means connected, dependent, related.
The inner query depends on the outer query.
Example:
SELECT EmployeeName
FROM Employees E
WHERE Salary >
(
SELECT AVG(Salary)
FROM Employees
WHERE DepartmentID = E.DepartmentID
);See E.DepartmentID inside the subquery? That's the correlation. The inner query reads from the outer query's current row.
How They Actually Work
Let's walk through this:
SELECT EmployeeName
FROM Employees E
WHERE Salary >
(
SELECT AVG(Salary)
FROM Employees
WHERE DepartmentID = E.DepartmentID
);Execution flow:
Row 1 → Calculate Department Average
↓
Compare Salary
Row 2 → Calculate Department Average
↓
Compare Salary
Row 3 → Calculate Department Average
↓
Compare SalaryFor each employee, the subquery recalculates the average for that employee's department.
Visual Flow
Outer Query Row
│
↓
Correlated Subquery Executes
│
↓
Returns Result
│
↓
Row Evaluated
│
↓
Next RowThis repeats until all rows are checked.
Setting Up Sample Tables
CREATE TABLE Employees (
EmployeeID INT,
EmployeeName VARCHAR(100),
DepartmentID INT,
Salary DECIMAL(10,2)
);Add some data:
INSERT INTO Employees VALUES
(1, 'Rahul', 1, 50000),
(2, 'Priya', 1, 70000),
(3, 'Amit', 2, 60000),
(4, 'Neha', 2, 90000);Your First Correlated Subquery
Goal: Find employees earning more than their department average.
Query:
SELECT EmployeeName,
Salary
FROM Employees E
WHERE Salary >
(
SELECT AVG(Salary)
FROM Employees
WHERE DepartmentID = E.DepartmentID
);Step-by-Step Breakdown
Department 1:
| Employee | Salary |
|---|---|
| Rahul | 50000 |
| Priya | 70000 |
Average:
60000
Who's above average?
PriyaDepartment 2:
| Employee | Salary |
|---|---|
| Amit | 60000 |
| Neha | 90000 |
Average:
75000Who's above?
NehaFinal result:
| EmployeeName |
|---|
| -------------- |
| Priya |
| Neha |
Why a Simple Subquery Won't Work
Simple subquery approach:
SELECT EmployeeName
FROM Employees
WHERE Salary >
(
SELECT AVG(Salary)
FROM Employees
);This calculates:
One Average For Entire Company
But you need:
Different Average
For Each DepartmentOnly a correlated subquery solves this.
Correlated Subquery with EXISTS
Very common pattern. Check for existence:
SELECT CustomerName
FROM Customers C
WHERE EXISTS
(
SELECT 1
FROM Orders O
WHERE O.CustomerID =
C.CustomerID
);Translation:
Return Customers Who Have Orders
Each customer is checked individually.
Correlated Subquery with NOT EXISTS
Flip the logic:
SELECT CustomerName
FROM Customers C
WHERE NOT EXISTS
(
SELECT 1
FROM Orders O
WHERE O.CustomerID =
C.CustomerID
);Translation:
Return Customers Without Orders
Real-World: E-Commerce
Products:
| Product | Category | Price |
|---|---|---|
| Laptop | Electronics | 60000 |
| Phone | Electronics | 30000 |
| Chair | Furniture | 5000 |
| Sofa | Furniture | 20000 |
Want: Products more expensive than their category average.
Query:
SELECT ProductName
FROM Products P
WHERE Price >
(
SELECT AVG(Price)
FROM Products
WHERE CategoryID =
P.CategoryID
);Real-World: Education
Want: Students scoring above their class average.
Query:
SELECT StudentName
FROM Students S
WHERE Marks >
(
SELECT AVG(Marks)
FROM Students
WHERE ClassID =
S.ClassID
);Real-World: Banking
Want: Customers with balance higher than their branch average.
Query:
SELECT CustomerName
FROM Accounts A
WHERE Balance >
(
SELECT AVG(Balance)
FROM Accounts
WHERE BranchID =
A.BranchID
);Correlated Subquery in UPDATE
You can update conditionally using correlation:
UPDATE Employees E
SET Bonus = 5000
WHERE Salary >
(
SELECT AVG(Salary)
FROM Employees
WHERE DepartmentID =
E.DepartmentID
);Employees above their department average get a bonus.
Correlated Subquery in DELETE
Delete based on a condition:
DELETE FROM Employees E
WHERE Salary <
(
SELECT AVG(Salary)
FROM Employees
WHERE DepartmentID =
E.DepartmentID
);Removes employees below their department average.
Correlated vs Simple Subquery
Simple Subquery:
Executes Once
Example:
SELECT *
FROM Employees
WHERE Salary >
(
SELECT AVG(Salary)
FROM Employees
);Correlated Subquery:
Executes For Every Row
Example:
SELECT *
FROM Employees E
WHERE Salary >
(
SELECT AVG(Salary)
FROM Employees
WHERE DepartmentID = E.DepartmentID
);Performance Consideration
Correlated subqueries can be slower for large datasets because they execute repeatedly. In some cases, you might get better performance with JOINs.
But they're excellent for readability and certain problems are just easier to solve this way.
Key Takeaways
✅ Correlated subqueries reference the outer query
✅ They execute once per outer row
✅ Perfect for department/category/group comparisons
✅ Works with EXISTS, UPDATE, DELETE
✅ May be slower than JOINs on huge datasets
✅ Highly readable for business logic
Use correlated subqueries when you need per-row context and comparisons. They're a powerful tool.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Correlated Subquery.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this SQL Complete Guide topic.
Search Terms
sql-complete-guide, sql complete guide, sql, complete, guide, subqueries, correlated, subquery
Related SQL Complete Guide Topics