c2cedge
LibrarySQL & Databases › Ch 10
SQL & Databases · D — Advanced Querying

Common Table Expressions (CTEs)

A CTE names a query so you can build complex logic in readable steps — and, when recursive, walk hierarchies like org charts.

Test weight: Medium–HighSkill: Readable, layered queriesDifficulty: Medium–Hard

A Common Table Expression (CTE) is a named temporary result defined with WITH, used by the query that follows. CTEs make complex queries readable by breaking them into named steps, and a recursive CTE can traverse hierarchical data such as an employee-manager tree.

WITH ... AS (...)

A CTE reads top to bottom: define one or more named subqueries with WITH name AS (...), then reference them in the main query as if they were tables. It's clearer than nesting subqueries and the name can be reused within the statement.

A CTE for readability
WITH dept_avg AS (
    SELECT dept_id, AVG(salary) AS avg_sal
    FROM   employees
    GROUP  BY dept_id
)
SELECT e.name, e.salary, da.avg_sal
FROM   employees e
JOIN   dept_avg da ON e.dept_id = da.dept_id
WHERE  e.salary > da.avg_sal;
A recursive CTE: the management chain
WITH RECURSIVE chain AS (
    SELECT emp_id, name, manager_id, 1 AS lvl   -- anchor: top managers
    FROM   employees WHERE manager_id IS NULL
    UNION ALL
    SELECT e.emp_id, e.name, e.manager_id, c.lvl + 1
    FROM   employees e JOIN chain c ON e.manager_id = c.emp_id
)
SELECT * FROM chain ORDER BY lvl;
⚡ The edge
  • A CTE is a named subquery — it doesn't make queries faster, it makes them readable and avoids repeating the same subquery twice.
  • A recursive CTE has an anchor member (the starting rows) and a recursive member that joins back to the CTE, combined with UNION ALL — ideal for trees and hierarchies. It must eventually stop.
Worked example
Why use a CTE instead of a nested subquery?
  1. A deeply nested subquery is hard to read and may repeat the same logic.
  2. A CTE names that logic once at the top, so the main query reads cleanly and can reference it multiple times.
  3. It's about clarity and reuse, not performance — the optimiser often treats them similarly.
Worked example
What two parts make up a recursive CTE?
  1. The anchor member selects the starting rows (e.g. top-level managers).
  2. The recursive member references the CTE itself to extend the result one level at a time.
  3. They're combined with UNION ALL, and recursion stops when the recursive member returns no new rows.
⚠ Watch out
  • A CTE's scope is the single statement it precedes — it isn't a stored object like a view.
  • A recursive CTE must terminate; a wrong join can loop until it hits the recursion limit.
  • CTEs aren't automatically faster than subqueries — they're for clarity.
Practice this — take a timed mock →
1,300+ questions, scored, with a weak-area report.
Know who's ready. Not who finished.
HomeLibraryPrivacyTerms