VillageSQL is a drop-in replacement for MySQL with extensions.
All examples in this guide work on VillageSQL. Install Now →
WITH keyword. CTEs are fully supported in MySQL 8.4 and 9.x.
Basic CTE Syntax
WITH clause, and each can reference the ones defined before it:
CTEs vs Subqueries
Use CTEs when the subquery is complex, when you reference the same result more than once, or when nesting would make the query hard to follow. For a comparison of when inline subqueries are better, see Subqueries vs JOINs in MySQL.
Recursive CTEs
A recursive CTE references itself. This is the standard SQL pattern for querying hierarchical data — org charts, category trees, threaded comments.RECURSIVE keyword is required — without it, MySQL doesn’t recognize the self-reference and returns ERROR 1146: Table 'org_chart' doesn't exist. A recursive CTE has two parts joined by UNION ALL:
- Anchor member — the base case (employees with no manager)
- Recursive member — the step case (reports of employees already in the result)
cte_max_recursion_depth (default: 1000) and errors. Guard against this with a depth column and a WHERE depth < N condition.
Frequently Asked Questions
Are CTEs faster than subqueries?
Usually the same. The MySQL optimizer often inlines a CTE the same way it would a subquery. CTEs referenced multiple times may be materialized into a temporary table, which can be faster (avoids re-executing the subquery) or slower (extra I/O). If you suspect unwanted materialization, checkEXPLAIN for “MATERIALIZED” in the extra column.
Can I use CTEs in INSERT, UPDATE, or DELETE?
Yes. CTEs work with DML statements:What’s the recursion depth limit?
The default is 1000 iterations (cte_max_recursion_depth). For deeper hierarchies, increase the session variable: SET SESSION cte_max_recursion_depth = 5000. If you hit the limit on normal data, check that your recursive member has a proper termination condition and that the data doesn’t contain cycles.
Troubleshooting
See also
- Subqueries vs JOINs in MySQL — how CTEs compare to correlated subqueries
- Window Functions in MySQL 8.4 — often used together with CTEs for analytic queries
- GROUP BY and HAVING in MySQL — aggregation patterns CTEs can simplify

