A correlated subquery is a subquery whose inner query references columns of the outer query. Because the inner query runs each time the outer query processes a row, it can dynamically compute a per-row value such as "the maximum value for that row's user."
SELECT * FROM orders o1 WHERE ordered_at = ( SELECT MAX(ordered_at) -- compute the max for o1's user_id FROM orders o2 -- reference the same table under a different alias WHERE o2.user_id = o1.user_id -- reference an outer column (this is the "correlation") );
From the orders table, retrieve only each user's latest order (the row where ordered_at is the maximum). Return order_id, user_id, amount, status, ordered_at ordered by user_id ascending.
| order_id | user_id | amount | status | ordered_at |
|---|---|---|---|---|
| 101 | 1 | 8,000 | completed | 2024-05-01 |
| 102 | 1 | 12,000 | completed | 2024-05-20 |
| 103 | 2 | 3,500 | completed | 2024-05-10 |
| 104 | 3 | 9,500 | completed | 2024-05-15 |
| 105 | 3 | 11,000 | completed | 2024-05-25 |
| 106 | 4 | 2,000 | completed | 2024-05-08 |
| 107 | 1 | 15,000 | completed | 2024-06-10 |
Expected output (only each user's latest order, ordered by user_id ascending):
| order_id | user_id | amount | status | ordered_at |
|---|---|---|---|---|
| 107 | 1 | 15,000 | completed | 2024-06-10 |
| 103 | 2 | 3,500 | completed | 2024-05-10 |
| 105 | 3 | 11,000 | completed | 2024-05-25 |
| 106 | 4 | 2,000 | completed | 2024-05-08 |
※ user1's latest is 107 (06-10) → 101/102 excluded. user3's latest is 105 (05-25) → 104 excluded.
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free
The HAVING clause filters the result of a GROUP BY aggregation. WHERE filters before aggregation (per row); HAVING filters after aggregation (per group). By using a scalar SQ as the comparison value in the HAVING clause, you can dynamically compare an overall aggregate against a group aggregate.
SELECT col, AVG(amount) FROM orders GROUP BY col HAVING AVG(amount) > ( -- post-aggregation group condition SELECT AVG(amount) FROM orders -- scalar SQ: returns the overall average );
WHERE AVG(amount) > ... is a syntax error. Because WHERE is evaluated before GROUP BY, you cannot use aggregate functions in its condition. To filter after aggregation, you must use HAVING.From the orders table, limited to orders whose status is 'completed', retrieve the users whose per-user average order amount is higher than the overall average. Return user_id, avg_amount (the ROUNDed integer) ordered by avg_amount descending.
| order_id | user_id | amount | status |
|---|---|---|---|
| 101 | 1 | 8,000 | completed |
| 102 | 1 | 12,000 | completed |
| 103 | 2 | 3,500 | completed |
| 104 | 3 | 9,500 | completed |
| 105 | 3 | 11,000 | completed |
| 106 | 4 | 2,000 | completed |
| 107 | 1 | 15,000 | completed |
Expected output (users exceeding the overall average ≈ 8,714, ordered by avg_amount descending):
| user_id | avg_amount |
|---|---|
| 1 | 11,667 |
| 3 | 10,250 |
※ Overall AVG = (8000+12000+3500+9500+11000+2000+15000)÷7 ≈ 8,714. user2 (3,500) and user4 (2,000) are below the overall average and are excluded.
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free
By combining multiple EXISTS / NOT EXISTS with AND, as in WHERE EXISTS (...) AND NOT EXISTS (...), you can extract in a single query the "rows that satisfy condition A and do not satisfy condition B." This is a field pattern that makes the EXISTS from the basic edition (Q5) compound.
SELECT * FROM users u WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.user_id = u.user_id -- an order exists ) AND NOT EXISTS ( SELECT 1 FROM reviews r WHERE r.user_id = u.user_id -- and no review exists );
From the users table, retrieve users who have at least one completed order yet have never posted a review. Return user_id, name, plan ordered by user_id ascending.
| user_id | name | plan |
|---|---|---|
| 1 | Tanaka Taro | premium |
| 2 | Sato Hanako | free |
| 3 | Suzuki Ichiro | premium |
| 4 | Yamada Jiro | standard |
| 5 | Ito Saburo | free |
| order_id | user_id | status |
|---|---|---|
| 101 | 1 | completed |
| 102 | 1 | completed |
| 103 | 2 | completed |
| 104 | 3 | completed |
| 105 | 3 | completed |
| 106 | 4 | completed |
| 107 | 1 | completed |
| review_id | user_id | order_id | rating |
|---|---|---|---|
| 1 | 1 | 101 | 5 |
| 2 | 3 | 104 | 4 |
Expected output (has completed / no review, ordered by user_id ascending):
| user_id | name | plan |
|---|---|---|
| 2 | Sato Hanako | free |
| 4 | Yamada Jiro | standard |
※ user1 has review1 → excluded. user3 has review2 → excluded. user5 has 0 orders → EXISTS fails → excluded.
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free
In the basic edition Q4, a FROM-clause subquery (derived table) grouped and aggregated, and the outer WHERE filtered it. Extending this pattern further, INNER JOINing the aggregate result (derived table) with another master table to obtain aggregate values and related information in a single query is the most frequent pattern in practice.
SELECT m.name, s.total FROM master m INNER JOIN ( SELECT id, SUM(amount) AS total FROM transactions GROUP BY id ) AS s ON m.id = s.id; -- join the aggregate result with the master
Aggregate the orders table to obtain each user's order count, total amount, and average amount, and combine them with the name / plan of the users table to return the result. Consider completed orders only, and return user_id, name, plan, order_count, total_amount, avg_amount ordered by total_amount descending.
| user_id | name | plan |
|---|---|---|
| 1 | Tanaka Taro | premium |
| 2 | Sato Hanako | free |
| 3 | Suzuki Ichiro | premium |
| 4 | Yamada Jiro | standard |
| 5 | Ito Saburo | free |
| order_id | user_id | amount | status |
|---|---|---|---|
| 101 | 1 | 8,000 | completed |
| 102 | 1 | 12,000 | completed |
| 103 | 2 | 3,500 | completed |
| 104 | 3 | 9,500 | completed |
| 105 | 3 | 11,000 | completed |
| 106 | 4 | 2,000 | completed |
| 107 | 1 | 15,000 | completed |
Expected output (aggregated over completed orders only, ordered by total_amount descending):
| user_id | name | plan | order_count | total_amount | avg_amount |
|---|---|---|---|---|---|
| 1 | Tanaka Taro | premium | 3 | 35,000 | 11,667 |
| 3 | Suzuki Ichiro | premium | 2 | 20,500 | 10,250 |
| 2 | Sato Hanako | free | 1 | 3,500 | 3,500 |
| 4 | Yamada Jiro | standard | 1 | 2,000 | 2,000 |
※ user5 has no completed orders, so no row is generated in the derived table and it is automatically excluded by the INNER JOIN.
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free
A subquery can contain yet another subquery (multi-level nesting). By narrowing step by step from the outside inward — "extract the category's maximum sales → identify that category name → extract that category's products" — you can express complex conditions.
SELECT * FROM products WHERE category = ( SELECT category -- ② find the category name FROM sales_summary WHERE total = ( SELECT MAX(total) -- ① fix the maximum value first FROM sales_summary ) );
Using the following 3 tables, retrieve the product list of the category with the largest total sold quantity (qty) among completed orders (completed). Return product_id, name, category, price ordered by price descending.
| product_id | name | category | price |
|---|---|---|---|
| 1 | Wireless Earbuds | electronics | 8,000 |
| 2 | Smartwatch | electronics | 25,000 |
| 3 | Cotton T-shirt | apparel | 3,500 |
| 4 | Denim Jacket | apparel | 12,000 |
| 5 | Protein Powder | health | 5,000 |
| order_id | status |
|---|---|
| 101 | completed |
| 102 | completed |
| 103 | completed |
| 104 | pending |
| item_id | order_id | product_id | qty |
|---|---|---|---|
| 1 | 101 | 1 | 2 |
| 2 | 101 | 3 | 1 |
| 3 | 102 | 2 | 1 |
| 4 | 102 | 1 | 3 |
| 5 | 103 | 4 | 2 |
| 6 | 103 | 5 | 1 |
| 7 | 104 | 2 | 2 |
Expected output (products of the best-selling category = electronics, ordered by price descending):
| product_id | name | category | price |
|---|---|---|---|
| 2 | Smartwatch | electronics | 25,000 |
| 1 | Wireless Earbuds | electronics | 8,000 |
※ Aggregated over completed orders only. electronics: qty total = 2+3+1 = 6, apparel: 1+2 = 3, health: 1. electronics is the largest. order_id=104 is pending and is excluded.
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free