In this file: defining more than one CTE in a single
WITHclause, the rules for chaining and comma-separating them, and joining two staged CTEs together for the first time.
How can multiple temporary datasets be prepared independently, then combined in one final query — instead of writing one query that does everything at once?
Real reporting queries rarely touch a single table. The moment a query needs
employees and departments and locations, a single monolithic
SELECT becomes hard to reason about: which JOIN filters which rows? A
WITH clause holding several CTEs lets you build the query the same way you
would explain it out loud — “first get the employees, then get the
departments, then join them” — with each stage isolated and independently
testable.
See 02_Multiple_CTEs.sql for all three queries.
WITH emp_cte AS (
SELECT emp_id, emp_name, dept_id
FROM employes
),
dept_cte AS (
SELECT dept_id, dept_name
FROM departments
)
SELECT
e.emp_id,
e.emp_name,
d.dept_name
FROM emp_cte e
JOIN dept_cte d
ON d.dept_id = e.dept_id;
Multiple CTEs live inside one WITH keyword, separated by commas — not
repeated WITH statements:
WITH first_cte AS ( ... ),
second_cte AS ( ... ),
third_cte AS ( ... )
SELECT ...
Two rules govern how they interact:
SELECT finishes.This file takes that pattern from two CTEs (emp_cte, dept_cte) to three
(adding locations_cte), previewing the join pattern the rest of the module
builds on.
Employee and department data were separated into individual, reusable
blocks and combined only in the final SELECT. Each block can be read,
tested, and debugged on its own — comment out the final JOIN and run
SELECT * FROM emp_cte in isolation to confirm it looks right before
trusting the combined result. This is the core habit that makes large SQL
queries maintainable: isolate a stage, verify it, then compose.
WITH clause — this raises a
syntax or ambiguity error depending on the engine.e.emp_id vs. d.dept_id) once two
CTEs share a column name like dept_id.Multiple CTEs are the backbone of most real-world reporting and dashboard queries. Interviewers use this pattern to test whether you can decompose a vague business ask (“show me headcount by department and city”) into discrete, joinable stages — rather than attempting one sprawling query in a single pass.
dept_cte itself, rather
than filtering the final result — compare the two approaches.Previous: 01_Basic_CTE.md · Next:
03_CTE_Joins.md — extending this pattern to a full
three-table join.