Last modified: May 4, 2025
Nested Queries
A subquery is a SQL query nested inside a larger query
Such inner-outer queries are called nested queries
A subquery may occur in:
- A SELECT clause
- A FROM clause
- A WHERE clause
In a SELECT clause, must return a single value (1 row, 1 column)
In a FROM clause, returns a relation (many rows, many relations), aliased using a tuple variable
In a WHERE clause, may return a single value to be compared with another value
In a WHERE clause, may return a relation to be used with WHERE EXISTS
Subqueries in SELECT (single value)
For each product return the city where it is manufactured
SELECT X.pname, (SELECT Y.city
FROM Company Y
WHERE Y.cid = X.cid) as City
FROM Product X
"Correlated subquery": a subquery that depends on a value from the outer query
"Using this product'scid
, find thecity
in theCompany
table."
Same thing as:
SELECT X.pname, Y.city
FROM Product X, Company Y
WHERE X.cid = Y.cid
Compute the number of products made by each company
SELECT C.cid, C.cname, (SELECT count(*)
FROM Product P
WHERE P.cid = C.cid)
FROM Company C
Same thing as:
SELECT C.cid, C.cname, count(pname)
FROM Company C LEFT JOIN Product P
ON C.cid = P.cid
GROUP BY C.cid, C.cname
Without LEFT JOIN, companies with no products are excluded
Subqueries in FROM
Find all products whose prices is > 20 and < 500
SELECT X.pname
FROM (SELECT *
FROM Product AS Y
WHERE price > 20) as X
WHERE X.price < 500
Every SQL returns a relation, so you can use it in the FROM clause like any other relation
Sometimes we need to compute an intermediate table only to use it later in a SELECT-FROM-WHERE
- Option 1: use a subquery in the FROM clause
- Option 2: use the WITH clause
WITH myTable AS (
SELECT *
FROM Product AS Y
WHERE price > 20)
SELECT X.pname
FROM myTable as X
WHERE X.price < 500
Subqueries in WHERE
Find all companies that make some products with price < 200
Using EXISTS:
SELECT C.cid, C.cname
FROM Company C
WHERE EXISTS (SELECT *
FROM Product P
WHERE C.cid = P.cid and P.price < 200)
EXISTS returns TRUE if the subquery returns any row
Using IN:
SELECT C.cid, C.cname
FROM Company C
WHERE C.cid IN (SELECT P.cid
FROM Product P
WHERE P.price < 200)
Using ANY:
SELECT C.cid, C.cname
FROM Company C
WHERE 200 > ANY (SELECT price
FROM Product P
WHERE P.cid = C.cid)
Not supported in some DBMS (avoid)
Unnest it:
SELECT DISTINCT C.cid, C.cname
FROM Company C, Product P
WHERE C.cid = P.cid and P.price < 200
Find all companies s.t. all their products have price < 200 = Find all companies that make only products with price < 200
- Find the other companies that make some product ≥ 200
SELECT C.cid, C.cname
FROM Company C
WHERE C.cid NOT IN (
SELECT P.cid
FROM Product P
WHERE P.price >= 200
)
- Find all companies s.t. all their products have price < 200
Another way using EXISTS
SELECT C.cid, C.cname
FROM Company C
WHERE NOT EXISTS (SELECT *
FROM Product P
WHERE P.cid = C.cid and P.price >= 200)
Using ALL:
SELECT C.cid, C.cname
FROM Company C
WHERE 200 >= ALL (SELECT price
FROM Product P
WHERE P.cid = C.cid)
Not supported in some DBMS (avoid)