Subqueries

Nest one query inside another for multi-step logic

Intermediate 16 minsubquerynested

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:

In WHERE (most common) — Filter based on a computed value: 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.
In FROM (derived table) — The subquery acts as a temporary 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.
In SELECT (scalar) — Compute a single value per row: (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.
Single vs. multiple rows — Scalar subqueries (returning one value) work with =, >, < operators. Multi-row subqueries need IN, EXISTS, ANY, or ALL to handle the multiple values returned.
Knowledge Canvas

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

1
Uncorrelated: inner query runs ONCE, result is reused
2
Correlated: inner query runs for EACH outer row
3
FROM subquery: creates a derived table (inline view)
4
Scalar in SELECT: computes one value per output row

Subquery vs JOIN vs CTE

Three ways to combine data

Subquery: nested, compact
JOIN: flat, performant
CTE: named, readable
Subquery: anonymous, inline
Correlated: per-row execution
JOIN: set-based execution
Use subquery for simple filters
Use JOIN for combining columns

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
Syntax
Syntax Template
1-- Scalar subquery in WHERE — returns one value
2SELECT *
3FROM products
4WHERE price > (
5 SELECT AVG(price)
6 FROM products
7);
8
9-- List subquery with IN — returns many values
10SELECT *
11FROM customers
12WHERE id IN (
13 SELECT customer_id
14 FROM orders
15 WHERE total > 100
16);
17
18-- Derived table in FROM — must have an alias
19SELECT sub.customer_id, sub.spend
20FROM (
21 SELECT
22 customer_id,
23 SUM(total) AS spend
24 FROM orders
25 GROUP BY customer_id
26) AS sub;
Scalar subqueryReturns one value — use with =, >, <
List subqueryReturns multiple values — use with IN, EXISTS
Derived tableSubquery in FROM, must have an alias
Sample Data
products
10 rows
idnameprice
P101Wireless Mouse24.99
P102Office Desk189
P103LED Desk Lamp45.5
P104USB-C Cable12.99
P105Bluetooth Speaker79
P106Office Chair95
P107Coffee Maker89.99
P108Notebook Set8.5
P109Wireless Headphones159
P110Standing Desk425
customers
12 rows
idnameemailcitycountry
C1001Aarav Sharma[email protected]MumbaiIndia
C1002Sara Chen[email protected]SingaporeSingapore
C1003James Wilson[email protected]LondonUK
C1004Maria Garcia[email protected]MadridSpain
C1005Yuki Tanaka[email protected]TokyoJapan
C1006Priya Patel[email protected]DelhiIndia
C1007Alex Johnson[email protected]New YorkUSA
C1008Chen Wei[email protected]ShanghaiChina
C1009Emma Brown[email protected]SydneyAustralia
C1010Omar Hassan[email protected]DubaiUAE
C1011Lena Muller[email protected]BerlinGermany
C1012Ravi Kumar[email protected]BangaloreIndia
orders
12 rows
idcustomer_idorder_datestatustotal
5001C10012024-01-15delivered59.98
5002C10022024-02-08delivered449
5003C10012024-02-20shipped12.5
5004C10032024-03-05delivered89.99
5005C10062024-03-15pending245
5006C10052024-03-22delivered159
5007C10042024-04-02shipped45.5
5008C10092024-04-12cancelled79
5009C10122024-04-22delivered425
5010C10072024-05-05pending18.99
5011C10082024-05-18delivered134
5012C10102024-06-08cancelled65.5

Worked Example

Find products that cost more than the average price.

SQL
1SELECT
2 name,
3 price
4FROM products
5WHERE price > (SELECT AVG(price) FROM products)
6ORDER BY
7 price DESC;
The inner query computes the average price (~105). The outer query then filters to products above that threshold. The subquery runs once, returns a single number, and the outer query uses it as a comparison value.
Output
3 rows
nameprice
Standing Desk425
Office Desk189
Wireless Headphones159
Common Mistakes

Subquery returns multiple rows with =

SQL
1SELECT
2 name,
3 price
4FROM products
5WHERE price = (SELECT price FROM products WHERE category = 'Electronics');

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

1Scalar subquery (1 value) → use with =, >, <
2List subquery (multiple rows) → use with IN, EXISTS, ANY
3Derived tables (FROM subquery) must always have an alias
4Inner query runs first — its result feeds the outer query

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.

Challenge

Solve the problem below

Find all customers who have placed at least one order over 200. Show their name. Use a subquery with IN.

Your Query