MySQL 8.0+才支持WITH语法,低于8.0.1版本执行会报语法错误;递归CTE必须显式使用WITH RECURSIVE,缺关键字或无终止条件均导致失败;推荐用于逻辑分层、树形结构等可读性与维护性优先的场景。

WITH在MySQL 8.0+才可用,低于这个版本会报错
MySQL直到8.0.1版本才原生支持WITH语法,如果你执行WITH t AS (SELECT 1) SELECT * FROM t却遇到ERROR 1064 (42000): You have an error in your SQL syntax,大概率是MySQL版本太低。用SELECT VERSION()确认一下,低于8.0.1就别硬试了——临时表或子查询是更稳妥的替代方案。
递归CTE必须显式声明RECURSIVE关键字
MySQL要求递归查询必须写WITH RECURSIVE,漏掉RECURSIVE会导致语法错误,不像PostgreSQL可以省略。比如生成1到5的数字序列:
WITH RECURSIVE nums(n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM nums WHERE n < 5 ) SELECT * FROM nums;
注意三点:nums必须有列别名((n));递归部分不能用ORDER BY或LIMIT;默认最大递归深度是1000,超限会报ERROR 3636 (HY000): Recursive query aborted after 1001 iterations,可通过SET SESSION cte_max_recursion_depth = 2000临时调高。
多个CTE之间用逗号分隔,不能用多个WITH
想定义两个临时结果集,不能写两次WITH,而要用逗号连接。下面这种写法是错的:
WITH a AS (SELECT 1) WITH b AS (SELECT 2) SELECT * FROM a, b;
正确写法是:
WITH a AS (SELECT 1), b AS (SELECT 2) SELECT * FROM a, b;
另外,CTE只在紧随其后的单条语句中有效,不能跨SELECT复用;如果后续还要用,得重复定义,或者改用视图/临时表。
CTE不是物化视图,性能未必比子查询好
很多人以为CTE“更清晰=更高效”,其实MySQL优化器对CTE的处理方式和子查询接近,甚至可能更慢——尤其当CTE被多次引用时,MySQL默认不缓存结果(除非加MATERIALIZED提示,但仅限8.0.23+)。例如:
WITH sales AS (SELECT * FROM orders WHERE status = 'paid') SELECT COUNT(*) FROM sales WHERE amount > 100 UNION ALL SELECT AVG(amount) FROM sales;
上面的sales会被执行两次。若数据量大,不如先建临时表,或确保WHERE条件能走索引。真正值得用CTE的场景是:逻辑分层明显(比如先清洗再聚合)、递归结构、或避免嵌套过深导致可读性崩坏。


















