sql server递归cte必须用with+union all结构,with须为分支首句,列类型严格一致,强制用union all防去重丢失数据,maxrecursion须置于最终语句末尾,缩进用replicate更高效。

SQL Server 存储过程中不能用 WITH RECURSIVE,直接抄 PostgreSQL 或 MySQL 写法会报 Incorrect syntax near the keyword 'RECURSIVE';必须用无 RECURSIVE 关键字的 WITH + UNION ALL 结构,且 WITH 必须是分支内第一条语句。
SQL Server 存储过程里写递归 CTE 为什么总报语法错误
常见错误不是逻辑错,而是语句位置违规。SQL Server 要求 WITH 必须是批处理(或 IF/BEGIN...END 分支)中的第一个可执行关键字。
- 错:在
IF @rootId IS NOT NULL BEGIN SELECT 1; WITH Tree AS (...) ... END——WITH前有SELECT,直接报错 - 对:把整个
WITH块提前到分支开头:IF @rootId IS NOT NULL BEGIN WITH Tree AS (...) SELECT * FROM Tree OPTION (MAXRECURSION 500); END - 若需多条件分支(比如按
@mode = 'down'或'up'查不同方向),不要在一个 CTE 里塞所有逻辑,应拆成两个独立WITH块,各自控制锚点和 JOIN 条件
UNION ALL 不是可选项,是强制要求
递归 CTE 中必须用 UNION ALL,不是风格问题,是 SQL Server 执行语义决定的:它靠逐层追加结果来模拟迭代,UNION 的去重逻辑会误删同名节点,导致树断裂。
- 假设某部门下有两个同名员工
name = '李工',用UNION后第二层只留一个,后续子树全丢 - 执行计划里若出现
Sort算子在递归分支中,基本就是误用了UNION - 锚成员和递归成员的列名、数据类型必须严格一致,否则
UNION ALL会隐式转换失败(比如INT和VARCHAR拼接时没显式CAST)
MAXRECURSION 不加就卡死,加错位置也没用
OPTION (MAXRECURSION n) 必须写在最终 SELECT、INSERT 或 UPDATE 语句末尾,不能放在 CTE 定义里,也不能漏掉。
- 默认上限是 100 层,查 5 级以上组织架构或评论嵌套极易触发
The maximum recursion 100 has been exhausted -
OPTION (MAXRECURSION 0)表示不限制,但生产环境禁用——循环引用或脏数据会导致会话卡死、内存耗尽 - 建议把
@maxRecursion设为存储过程输入参数,由调用方按业务控制,例如“最多展开 6 级”就传6
树形缩进显示别用自连接,用 REPLICATE 更稳
前端要层级缩进(如 ├─ 研发部、│ ├─ 一组),最可靠方式是在 CTE 里用 REPLICATE 拼空格或符号,而不是靠多次 LEFT JOIN 自关联模拟。
- 示例:在递归 CTE 中加一列
level(从 0 开始),再用REPLICATE(' ', level) + name生成缩进文本 - 自连接方式在深度 > 4 层后性能断崖式下降,且无法动态控制缩进字符
- 如果需要路径字符串(如
/总公司/研发部/一组),直接在 CTE 里用CONCAT或STRING_AGG(SQL Server 2017+)拼接,别在存储过程里用循环累加
真正容易被忽略的是:CTE 本身不支持参数化重用,如果你在存储过程中反复查不同根节点,不要试图在一个 CTE 里用 CASE WHEN 切换锚点,而应让锚查询直接受参数驱动(如 WHERE id = @rootId),否则优化器可能无法正确估算行数,导致执行计划退化。











