SQL Server必须用无RECURSIVE关键字的WITH+UNION ALL结构,WITH须为首条语句,递归支中必须显式引用自身CTE名,三者缺一不可;锚与递归成员列数类型顺序一致,且需加OPTION(MAXRECURSION n)防无限循环。

SQL Server 不能用 WITH RECURSIVE,抄 PostgreSQL 或 MySQL 写法会直接报错;必须用无 RECURSIVE 关键字的 WITH + UNION ALL 结构,且递归支中必须显式引用自身 CTE 名——三者缺一不可。
SQL Server 里怎么写合法的递归 CTE
SQL Server 不认 WITH RECURSIVE,也不允许把 WITH 套在 IF 或 BEGIN 后面。它只接受最简硬规则:
-
WITH必须是批处理或存储过程分支里的第一条可执行语句(前面不能有注释、空行、DECLARE) - 锚查询和递归查询必须用
UNION ALL连接,不能用UNION(否则去重会丢子节点) - 递归查询里必须出现
JOIN Tree ON ...这种形式,其中Tree是你定义的 CTE 名;写成JOIN employees ON ...就不是递归 - 锚查询必须带明确终止起点,比如
WHERE manager_id IS NULL(查所有根)或WHERE id = @rootId(查单棵树)
层级字段和路径拼接必须在 CTE 里算好
光查出父子关系不够,前端缩进、排序、搜索路径都依赖 level 和 path 字段。这两个不能留到外层 SELECT 再加工,否则会破坏递归逻辑:
-
level从1开始,递归支里写t.level + 1;避免用0起始,否则和OPTION (MAXRECURSION n)默认行为容易混淆 -
path推荐用CONCAT('/', e.id)初始化,递归支用CONCAT(t.path, '/', e.id);这样生成的字符串可排序、可LIKE '/1/2/%'查询 - 如果主键是字符串(如
'DEPT-001'),path字段必须声明为VARCHAR(500)这类定长类型,否则超长被截断后路径失效 - 别在 CTE 里用
REPLICATE('─', level * 2)拼前缀——那是展示层逻辑,CTE 只管结构
存储过程中调用时最容易踩的坑
在存储过程里写递归 CTE,比在 SSMS 里更容易翻车,因为语句位置约束更严:
- 下面这种写法会报
Incorrect syntax near 'WITH': IF @rootId IS NOT NULL BEGIN -- 注释或空行在这里就违规 WITH Tree AS () SELECT * FROM Tree; END- 动态根节点别拼 SQL:用
WHERE id = @rootId,而不是EXEC('WITH Tree AS () ... WHERE id = ' + @rootId)—— 动态 SQL 无法复用执行计划,还可能被注入 -
OPTION (MAXRECURSION n)必须加在最终SELECT末尾,不能放在 CTE 定义里;默认 100 层不够时,设成500更稳妥,但禁止用0(无限制)
不同数据库的关键语法差异
跨数据库移植递归 CTE 时,三个引擎的核心区别必须盯死:
- PostgreSQL:必须写
WITH RECURSIVE,UNION ALL是可选但推荐 - MySQL 8.0+:可写可不写
RECURSIVE,引擎自动识别,但写了更清晰 - SQL Server:严禁出现
RECURSIVE关键字,且UNION ALL是强制要求,漏掉ALL就不是递归
复杂点不在语法本身,而在于:SQL Server 的递归 CTE 是“语句级结构”,不是变量也不是函数,它没有作用域嵌套概念,但对位置、顺序、引用名极度敏感——写错一个字符,它就拒绝执行,不会给你模糊提示。










