ποΈ 02092025 1020
π
sql_cte
π SQL CTE Cheatsheet
1. What is a CTE?β
A Common Table Expression (CTE) is a temporary result set defined using WITH
and used within a single query.
Think of it like creating a named subquery that improves readability and reusability.
WITH recent_orders AS ( SELECT id, user_id, created_at FROM orders WHERE created_at > NOW() - INTERVAL '30 days' ) SELECT user_id, COUNT(*) AS total_orders FROM recent_orders GROUP BY user_id;
Key traits β
- Defined once, referenced many times.
- Scope = the main query only.
- Can chain multiple CTEs in one
WITH
block.
2. Why Use a CTE?β
Use Case | Why CTEs Help |
---|---|
Readability | Break complex queries into logical steps |
Reusability | Avoid repeating the same subquery multiple times |
Composability | Chain transformations easily |
Recursive traversal | Perfect for trees, hierarchies, and graphs |
3. Non-Recursive CTEsβ
Most CTEs are non-recursive β evaluated once like a normal subquery.
WITH top_customers AS ( SELECT user_id, SUM(amount) AS total_spent FROM orders GROUP BY user_id HAVING SUM(amount) > 5000 ) SELECT u.name, t.total_spent FROM users u JOIN top_customers t ON u.id = t.user_id;
Execution π§
- CTE is materialized once.
- The query planner inlines it in many engines (Postgres, MySQL 8+).
- Acts like a temporary view.
4. Recursive CTEsβ
A CTE becomes recursive when it references itself in its definition.
Used for hierarchical data, graphs, paths, ancestor/descendant trees.
Structureβ
WITH RECURSIVE cte_name AS ( -- Anchor (base case) SELECT ... FROM ... WHERE ... UNION ALL -- Recursive member (self-referencing step) SELECT ... FROM ... JOIN cte_name c ON ... ) SELECT * FROM cte_name;
Key points β
- Requires
WITH RECURSIVE
(in Postgres/MySQL 8+). - Anchor = starting rows.
- Recursive step = generates the βnext levelβ.
- Stops when the recursive step returns 0 new rows.
5. Simple Recursive Exampleβ
Problem: Get all employees reporting under a specific manager.
WITH RECURSIVE subordinates AS ( -- Anchor: start from the given manager SELECT id, name, manager_id FROM employees WHERE id = 1 -- CEO UNION ALL -- Recursive step: find direct reports SELECT e.id, e.name, e.manager_id FROM employees e JOIN subordinates s ON e.manager_id = s.id ) SELECT * FROM subordinates;
How it works π§
- Start with
id=1
(anchor row). - Find employees where
manager_id=1
. - For each result, repeat the process.
- Stops when there are no more reports.
6. How the Engine Executes Recursive CTEsβ
- Engine splits the recursive CTE into:
- Anchor member β initial result set.
- Recursive member β applied repeatedly.
- Maintains a working table for βnew rowsβ.
- Iterates until no more rows are added.
Iteration 1 β Anchor rows Iteration 2 β Anchor + Recursive step 1 Iteration 3 β Previous + Recursive step 2 ... Stops when Recursive step returns 0 rows.
7. Performance Tipsβ
Tip | Why |
---|---|
Use UNION ALL instead of UNION | Avoids unnecessary de-duplication work |
Deduplicate early (DISTINCT ) | Prevents exponential growth |
Carry fewer columns | Smaller working sets = less memory |
Add indexes on join keys | Recursive joins become faster |
Limit recursion depth if needed | Prevent runaway loops |
8. Quick Summary Tableβ
Type | Keyword | Self-Reference | Evaluated | Use Case |
---|---|---|---|---|
Non-recursive | WITH | β No | Once | Simplify queries |
Recursive | WITH RECURSIVE | β Yes | Iteratively | Trees, graphs |
Do you want me to also make a visual diagram showing: