Skip to content
wannacode

Subqueries

In short: A subquery is a SELECT inside another query that returns one value or a list for WHERE, used with IN, EXISTS or NOT EXISTS.

Problem

Above average

The manager wants to see the products that cost more than the average price in the shop. You already know two queries: one finds the average price, the other looks for products above a given number.

You could run the first, copy the number and run the second. But tomorrow the prices will be different, and the copied number will be out of date.

The idea

Subquery

First the average price:

SQLdatabase: shop

Now the same query, in brackets, in place of the number:

SQLdatabase: shop

A query in brackets inside another query is called a subquery. The database runs it first, gets 7.036, and then checks every product with price > 7.036. Eight products come out.

This subquery returns one number, so it goes where a number would go.

More precisely, if you are curious

If a subquery in a comparison returns several rows, SQLite quietly takes the first one. Category 8 (“Kitchen”) has three products, but the query compares with only one of them:

SQLdatabase: shop

Which row is “the first” is not promised. PostgreSQL and SQL Server give an error in this case, saying the subquery returned more than one row. If a subquery can return several rows, use IN.

Try it

Pricier than the kashkaval

Show the name and price of the products that cost more than the kashkaval cheese. Do not type its price by hand: get it with a subquery.

SQLdatabase: shop

The idea

IN with a list from a query

You already know IN with a list typed by hand: city IN ('Varna', 'Burgas'). The list can also come from a subquery.

Which customers ordered in October 2026? Their numbers are in orders, and their names are in customers:

SQLdatabase: shop

The subquery returns the numbers of the customers with orders from 1 October on. The outer query shows the customers whose id is in that list.

Subqueries can be nested inside each other. Each step is a separate query that you can run and check on its own.

Try it

Who buys coffee

The coffee supplier is launching a new flavour and wants to write to the people who have already bought “Coffee beans”. Show their names. The field starts with the first step: the product number.

SQLdatabase: shop

The idea

EXISTS

Sometimes you do not care what the subquery returns, only whether it returns anything. Which customers have no orders yet?

SQLdatabase: shop

EXISTS (…) is true if the subquery returns at least one row. NOT EXISTS is the opposite. What comes after SELECT in the subquery does not matter, so it is SELECT 1.

Here the subquery looks at the outer row: o.customer_id = c.id, where c is the customer that the outer query is checking right now. This is a new idea, and the next lesson looks at it closely.

See it

NOT IN and a single NULL

The manager is arranging the menu of the website and asks which categories have no products at all. They expect “Food” and “Home”: those hold only subcategories. Here is the first attempt:

SQLdatabase: shop
1. Why did NOT IN return nothing instead of “Food” and “Home”?

This is the trap from the NULL lesson, but now NULL comes from the subquery and cannot be seen in the query. NOT EXISTS compares no values: it asks whether there is such a product, and NULL does not get in its way:

SQLdatabase: shop

When the subquery is over a column that can be NULL, use NOT EXISTS or add WHERE category_id IS NOT NULL to the subquery.

Exercise

Two tasks

1. The quality team wants to call the customers who have at least one cancelled order.

Show the names of the customers with at least one cancelled order.

SQLdatabase: shop

2. Marketing is preparing a “Write a review” campaign for the products nobody has reviewed yet.

Show the names of the products without a single review (the reviews table).

SQLdatabase: shop

Test

Check yourself

1. What must the subquery in WHERE price > (…) return?
2. What does EXISTS (subquery) check?
3. The subquery in NOT IN (…) may return NULL. What is safer?
4. Why is WHERE price > (SELECT AVG(price) FROM products) better than WHERE price > 7.036?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·