Lesson 6 / 10

Subqueries and Common Table Expressions

A subquery inside WHERE

SELECT name
FROM students
WHERE score > (SELECT AVG(score) FROM students);

The inner query runs first, producing a single value (the class average), which the outer query then compares each row against. A subquery like this can return a single value, a single column of many values (for use with IN), or an entire table (for use in a FROM clause) — the syntax adapts to where you place it.

A subquery with IN

SELECT name
FROM students
WHERE course_id IN (
  SELECT id FROM courses WHERE level = 'Advanced'
);

Common Table Expressions (CTEs)

WITH high_scorers AS (
  SELECT * FROM students WHERE score >= 90
)
SELECT course, COUNT(*)
FROM high_scorers
GROUP BY course;

A CTE, introduced with WITH, names a temporary result set you can then query exactly like a real table for the rest of that single query. It makes multi-step logic far easier to read than nesting several subqueries inside each other, and you can even chain multiple CTEs together, each one building on the last.

CTEs vs subqueries

A CTE and an equivalent subquery are usually processed identically by the database — the difference is almost entirely about readability. Once a query needs more than one level of nested logic, reach for a CTE; your future self (and your teammates) will read it far faster than a deeply nested subquery.