Common Table Expressions (CTEs) in SQL
Common Table Expressions (CTEs) in SQL — A Beginner's Guide
A Common Table Expression (CTE) is a named, temporary result set you can reference within a single query. Think of it as giving a subquery a name and using that name like a regular table — it makes complex queries dramatically more readable.
๐ Basic Syntax
WITH high_earners AS (
SELECT employee_id, name, salary
FROM employees
WHERE salary > 80000
)
SELECT * FROM high_earners
WHERE name LIKE 'A%';
The WITH ... AS (...) block defines the CTE, and it can then be queried just like a table in the main SELECT below it.
๐ฏ Why Use a CTE Instead of a Subquery?
Compare the same logic without a CTE:
-- Without CTE — nested and harder to read
SELECT * FROM (
SELECT employee_id, name, salary
FROM employees
WHERE salary > 80000
) high_earners
WHERE name LIKE 'A%';
Functionally identical, but for anything more complex than this toy example, nested subqueries get hard to follow fast. CTEs let you name each logical step, top to bottom, like a recipe rather than a deeply nested set of parentheses.
๐งช Multiple CTEs in One Query
WITH regional_sales AS (
SELECT region, SUM(amount) AS total_sales
FROM orders
GROUP BY region
),
top_regions AS (
SELECT region
FROM regional_sales
WHERE total_sales > 100000
)
SELECT o.*
FROM orders o
JOIN top_regions t ON o.region = t.region;
Each CTE can reference the ones defined before it, letting you break a complex transformation into clear, named steps.
๐ Recursive CTEs
CTEs can also reference themselves, which is how you handle hierarchical data (org charts, category trees, bill-of-materials structures):
WITH RECURSIVE org_chart AS (
-- Anchor: top-level employees (no manager)
SELECT employee_id, name, manager_id, 1 AS level
FROM employees
WHERE manager_id IS NULL
UNION ALL
-- Recursive part: employees whose manager is already in org_chart
SELECT e.employee_id, e.name, e.manager_id, oc.level + 1
FROM employees e
JOIN org_chart oc ON e.manager_id = oc.employee_id
)
SELECT * FROM org_chart
ORDER BY level;
This walks down the management hierarchy level by level — something that's genuinely difficult to express any other way in plain SQL.
⚠️ Common Mistakes
- Assuming a CTE is materialized/cached like a temp table — in most databases (including BigQuery), a CTE is re-evaluated each time it's referenced, so referencing the same CTE multiple times can mean repeated computation
- Forgetting the
RECURSIVEkeyword when writing a self-referencing CTE — syntax requirements vary slightly by database - Building deeply nested CTEs when a simple intermediate table would be clearer and better for reuse across multiple queries
๐ Related Posts
- SQL JOIN Types Explained (INNER, LEFT, RIGHT, FULL) with Examples
- SQL Window Functions Explained: ROW_NUMBER, RANK, LAG, LEAD
Comments
Post a Comment