Subqueries
Nest one query inside another for multi-step logic
Queries Inside Queries
A subquery is a complete SELECT statement placed inside another query. The inner query runs first, and its result feeds into the outer query. Here's where subqueries appear:
WHERE price > (SELECT AVG(price) FROM products). The subquery computes the average price first, then the outer query uses that number to filter the products table.FROM (SELECT customer_id, SUM(total) AS spend FROM orders GROUP BY customer_id) AS sub. The alias is always required when using subqueries in FROM.(SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.id) AS order_count. This is a "correlated" subquery because it references the outer table.=, >, < operators. Multi-row subqueries need IN, EXISTS, ANY, or ALL to handle the multiple values returned.How Subqueries Work
Nest one query inside another
- A subquery is a SELECT inside another SQL statement
- Can appear in WHERE, FROM, SELECT, or HAVING
- Scalar subquery returns one value; table subquery returns rows
- Correlated subqueries reference the outer query — run per row
Execution Flow
How the database processes nested queries
Subquery vs JOIN vs CTE
Three ways to combine data
Subquery Pitfalls
Where things go wrong
- Scalar subquery returning multiple rows → error
- Correlated subqueries can be extremely slow
- Deep nesting (3+ levels) → unreadable — use CTEs
= (subquery)errors if subquery returns more than one row;IN (subquery)accepts any number of rows
Performance Notes
Subquery optimization
- Uncorrelated subqueries are optimized well by most engines
- Correlated subqueries: consider rewriting as JOIN
- EXISTS is often faster than IN for large subqueries
- Derived tables themselves aren't indexed, but the base tables inside the subquery still use their indexes
Scalar subqueryReturns one value — use with =, >, <List subqueryReturns multiple values — use with IN, EXISTSDerived tableSubquery in FROM, must have an alias| id | name | price |
|---|---|---|
| P101 | Wireless Mouse | 24.99 |
| P102 | Office Desk | 189 |
| P103 | LED Desk Lamp | 45.5 |
| P104 | USB-C Cable | 12.99 |
| P105 | Bluetooth Speaker | 79 |
| P106 | Office Chair | 95 |
| P107 | Coffee Maker | 89.99 |
| P108 | Notebook Set | 8.5 |
| P109 | Wireless Headphones | 159 |
| P110 | Standing Desk | 425 |
| id | name | city | country | |
|---|---|---|---|---|
| C1001 | Aarav Sharma | [email protected] | Mumbai | India |
| C1002 | Sara Chen | [email protected] | Singapore | Singapore |
| C1003 | James Wilson | [email protected] | London | UK |
| C1004 | Maria Garcia | [email protected] | Madrid | Spain |
| C1005 | Yuki Tanaka | [email protected] | Tokyo | Japan |
| C1006 | Priya Patel | [email protected] | Delhi | India |
| C1007 | Alex Johnson | [email protected] | New York | USA |
| C1008 | Chen Wei | [email protected] | Shanghai | China |
| C1009 | Emma Brown | [email protected] | Sydney | Australia |
| C1010 | Omar Hassan | [email protected] | Dubai | UAE |
| C1011 | Lena Muller | [email protected] | Berlin | Germany |
| C1012 | Ravi Kumar | [email protected] | Bangalore | India |
| id | customer_id | order_date | status | total |
|---|---|---|---|---|
| 5001 | C1001 | 2024-01-15 | delivered | 59.98 |
| 5002 | C1002 | 2024-02-08 | delivered | 449 |
| 5003 | C1001 | 2024-02-20 | shipped | 12.5 |
| 5004 | C1003 | 2024-03-05 | delivered | 89.99 |
| 5005 | C1006 | 2024-03-15 | pending | 245 |
| 5006 | C1005 | 2024-03-22 | delivered | 159 |
| 5007 | C1004 | 2024-04-02 | shipped | 45.5 |
| 5008 | C1009 | 2024-04-12 | cancelled | 79 |
| 5009 | C1012 | 2024-04-22 | delivered | 425 |
| 5010 | C1007 | 2024-05-05 | pending | 18.99 |
| 5011 | C1008 | 2024-05-18 | delivered | 134 |
| 5012 | C1010 | 2024-06-08 | cancelled | 65.5 |
Worked Example
Find products that cost more than the average price.
| name | price |
|---|---|
| Standing Desk | 425 |
| Office Desk | 189 |
| Wireless Headphones | 159 |
Subquery returns multiple rows with =
The subquery returns multiple prices (one per Electronics product) but = expects exactly one value. Run it — SQLite raises a 'sub-select returns N columns - expected 1' or returns wrong rows depending on the row order.
Use IN instead of = when the subquery can return multiple rows.
Key Concepts
Pro Tip
Many questions need two-step reasoning: compute something first, then use it. Subqueries express this naturally.
When to Use
Finding above-average items, filtering by computed thresholds, inline summaries.
Find all customers who have placed at least one order over 200. Show their name. Use a subquery with IN.