mysql 8.0+不支持insert直接嵌套递归cte,必须用insert ... select包裹with recursive子查询,且cte须紧邻select、列名类型严格一致、终止条件不可缺。

为什么直接用递归CTE INSERT会报错
PostgreSQL 和 SQL Server 支持 WITH RECURSIVE + INSERT 一步完成,但 MySQL(8.0+)和 SQLite 虽然支持递归 CTE,却**不支持在 INSERT 语句中直接嵌套递归 CTE**。常见错误是写成:
INSERT INTO t (n) WITH RECURSIVE seq(n) AS (...)
结果收到类似 ERROR 1235 (42000): This version of MySQL doesn't yet support 'INSERT ... SELECT ... WITH RECURSIVE' —— 不是语法写错,是引擎限制。
MySQL 8.0+ 正确写法:先 WITH 再 INSERT SELECT
必须把递归 CTE 单独作为子查询,再用 INSERT ... SELECT 包裹。关键点是:CTE 必须紧邻 SELECT,不能跨语句。
-
WITH RECURSIVE必须放在INSERT后、SELECT前,且中间不能有分号或空行 - 递归锚点(anchor)和递归成员(recursive term)的列名、类型必须严格一致
- 务必加终止条件,比如
n ,否则触发 <code>ERROR 3636 (HY000): Recursive query aborted after 1001 iterations
示例:生成 1~10 的整数并插入 numbers 表:
WITH RECURSIVE seq(n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM seq WHERE n <h3>SQL Server 和 PostgreSQL 可以更直接</h3> <p>这两者允许 <code>INSERT</code> 与递归 CTE 在同一语句中,但细节仍有差异:</p>
- SQL Server 要求递归 CTE 前加
OPTION (MAXRECURSION n)控制深度,否则默认只跑 100 层 - PostgreSQL 默认无硬限制,但建议显式写
WHERE n 防止失控 - 三者都要求递归字段不能是
NULL,否则递归分支中断(例如SELECT n+1 FROM seq WHERE n IS NOT NULL AND n 更安全)
PostgreSQL 示例(一步到位):
WITH RECURSIVE seq(n) AS ( SELECT 1 UNION ALL SELECT n+1 FROM seq WHERE n <h3>INSERT 太多行时性能卡在哪</h3> <p>递归 CTE 本质是内存迭代,不是批量生成;当目标值超 10⁴ 级别,容易 OOM 或超时。这不是语法问题,而是执行模型限制:</p>
- MySQL 会把整个递归结果集先缓存在内存里,再一次性 INSERT —— 所以 10 万行可能直接报
ERROR 1114 (HY000): The table '#sql-xxx' is full - 更稳的做法是分批:用
WHERE n BETWEEN 1 AND 1000循环执行 100 次,或改用存储过程控制循环 - 如果只是填充测试数据,考虑用应用层生成 CSV 再
LOAD DATA INFILE(MySQL)或COPY(PostgreSQL),速度通常快一个数量级
真正容易被忽略的是:递归深度阈值、中间结果集是否含索引字段、以及目标表是否有触发器或外键约束——这些都会让看似简单的 INSERT 实际变慢十倍以上。











