Performance Implications
Performance Implications
Correlated subqueries can be performance killers because they execute once per outer row. If the outer query returns 10,000 rows, the subquery executes 10,000 times. This is known as the N+1 problem.
-- Slow: correlated subquery in SELECT
SELECT
order_id,
customer_id,
(
SELECT COUNT(*)
FROM orders o2
WHERE o2.customer_id = o1.customer_id
) AS total_orders
FROM orders o1;
The equivalent JOIN-based approach is often much faster:
-- Faster: using a JOIN
SELECT
o1.order_id,
o1.customer_id,
c.total_orders
FROM orders o1
JOIN (
SELECT customer_id, COUNT(*) AS total_orders
FROM orders
GROUP BY customer_id
) c ON o1.customer_id = c.customer_id;
To improve correlated subquery performance:
- Ensure indexes exist on the columns used in the correlation
- Consider rewriting as a JOIN or window function
- Use EXISTS instead of IN for existence checks
- Limit the outer query result set when possible
-- Add an index to speed up the correlation
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
Window functions like COUNT(*) OVER (PARTITION BY customer_id) are typically the fastest alternative to correlated subqueries in SELECT clauses.
Practice Problems
Write a query to find all employees who earn more than the average salary in their own department. Return employee_name, department_id, and salary.
Solution
SELECT employee_name, department_id, salary
FROM employees e1
WHERE salary > (
SELECT AVG(salary)
FROM employees e2
WHERE e2.department_id = e1.department_id
);Write a query to find the most recent order for each customer. Return customer_id, order_id, order_date, and total_amount.
Solution
SELECT customer_id, order_id, order_date, total_amount
FROM orders o1
WHERE order_date = (
SELECT MAX(order_date)
FROM orders o2
WHERE o2.customer_id = o1.customer_id
);Write a query to find products whose average rating is above the average rating of all products in the same category. Return product_id, product_name, and average_rating.
Solution
SELECT p.product_id, p.product_name, pr.avg_rating
FROM products p
JOIN (
SELECT product_id, AVG(rating) AS avg_rating
FROM reviews
GROUP BY product_id
) pr ON p.product_id = pr.product_id
WHERE pr.avg_rating > (
SELECT AVG(r2.rating)
FROM reviews r2
JOIN products p2 ON r2.product_id = p2.product_id
WHERE p2.category = p.category
);Quiz
1. What makes a subquery 'correlated'?
2. How many times does a correlated subquery execute?
3. Which is generally faster for existence checks?
4. What is the primary purpose of SQL Correlated Subqueries?
Flashcards
Question
What is a correlated subquery?
Click to reveal answer
Answer
A subquery that references a column from the outer query, making it execute once per outer row instead of once.
Question
Why are correlated subqueries slower than non-correlated ones?
Click to reveal answer
Answer
They execute once per outer row (N+1 problem), whereas non-correlated subqueries execute only once and can cache results.
Question
What can you use instead of a correlated subquery in SELECT?
Click to reveal answer
Answer
Window functions (e.g., COUNT(*) OVER (PARTITION BY col)) or JOINs with aggregated derived tables.
Question
What is SQL Correlated Subqueries?
Click to reveal answer
Answer
SQL Correlated Subqueries is a key concept in SQL databases.
Question
When to use SQL Correlated Subqueries?
Click to reveal answer
Answer
Use SQL Correlated Subqueries when building production systems that require reliability, scalability, and maintainability.
Revision Notes
Key Takeaways
- 1.Correlated subqueries reference the outer query and execute per row
- 2.EXISTS is preferred for correlated existence checks
- 3.Consider rewriting as JOINs or window functions for performance
- 4.Always index columns used in correlations
Interview Tips
- •Explain the N+1 query problem caused by correlated subqueries
- •Show how to rewrite a correlated subquery as a JOIN
- •Discuss when EXISTS is better than IN
- •Mention window functions as modern alternatives
Cheat Sheet
Correlated Subqueries Cheat Sheet
Basic Pattern
SELECT * FROM outer o
WHERE col > (
SELECT AGG(col) FROM inner i
WHERE i.id = o.id
);
EXISTS Pattern
SELECT * FROM outer o
WHERE EXISTS (
SELECT 1 FROM inner i WHERE i.id = o.id
);
Rewrite as JOIN
-- Correlated
SELECT o.*, (SELECT COUNT(*) FROM i WHERE i.id = o.id) FROM o;
-- JOIN equivalent
SELECT o.*, c.cnt FROM o JOIN (SELECT id, COUNT(*) AS cnt FROM i GROUP BY id) c ON o.id = c.id;
Key Points
- Executes once per outer row
- Always consider JOIN or window function alternatives
- Index correlation columns for better performance