Subqueries & CTEs
A query nested inside another — and the readable, named version of the same idea.
Intermediate
-- scalar: returns one value, usable anywhere an expression is
SELECT name, price, price - (SELECT AVG(price) FROM products) AS diff_from_avg
FROM products;
-- list: returns multiple rows, usable with IN
SELECT name FROM products
WHERE category_id IN (SELECT id FROM categories WHERE active = true);
SELECT c.name
FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.id AND o.amount > 1000
);
-- customers who have at least one order over 1000
| EXISTS | IN | |
|---|---|---|
| NULL handling | unaffected by NULLs in the subquery | NOT IN with any NULL in the list returns zero rows — a classic gotcha |
| Performance | typically stops at the first match | materializes the full list first (though modern planners often optimize this away) |
| Style | preferred for existence checks | preferred for small, fixed, known lists |
NOT IN + NULL trap is worth internalizing on its own: WHERE id NOT IN (SELECT id FROM t WHERE ...) silently returns zero rows if that subquery can ever produce a NULL. NOT EXISTS doesn't have this problem — prefer it for "not in this set" queries.WITH high_value_customers AS (
SELECT customer_id, SUM(amount) AS total
FROM orders
GROUP BY customer_id
HAVING SUM(amount) > 10000
)
SELECT c.name, h.total
FROM high_value_customers h
JOIN customers c ON c.id = h.customer_id
ORDER BY h.total DESC;
MATERIALIZED explicitly if you need that guarantee).WITH RECURSIVE org_chart AS (
-- base case: the top of the tree
SELECT id, name, manager_id, 1 AS depth
FROM employees
WHERE manager_id IS NULL
UNION ALL
-- recursive case: join back to org_chart itself
SELECT e.id, e.name, e.manager_id, oc.depth + 1
FROM employees e
JOIN org_chart oc ON e.manager_id = oc.id
)
SELECT * FROM org_chart ORDER BY depth, name;