CTEs
16 min
A CTE (Common Table Expression) — the WITH clause — names a
subquery's result so you can reference it like a table for the rest of
the query. It's the same computation a subquery does, written so it reads
top-to-bottom instead of inside-out.
WITH dept_avg AS (
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
)
SELECT * FROM dept_avg ORDER BY avg_salary DESC;
Once defined, a CTE behaves like any other table for the rest of the statement — you can filter it, group it, or join it to other tables.
The real advantage over a subquery shows up once you need the SAME
intermediate result more than once, or want to JOIN back against the
original table:
WITH dept_avg AS (
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
)
SELECT e.name
FROM employees e
JOIN dept_avg d ON e.department = d.department
WHERE e.salary > d.avg_salary;
Written as a nested subquery instead, this same logic would need the
AVG(salary) computation repeated or awkwardly duplicated — the CTE lets
you compute it once, name it, and join against it directly.
The `employees` table has columns `id, name, department, salary`. Using a CTE named `dept_avg`, write a query that returns each `department` and its average salary as `avg_salary`, ordered by avg_salary descending.
Using a CTE, write a query that returns the `name` of every employee who earns MORE than their own department's average salary, ordered by name.
What's the main readability benefit of a CTE (WITH clause) over an equivalent nested subquery?
You can define a CTE with WITH and reference it like a table, including joining it back against the original data.