SQL Server不支持存储过程自调用,应使用递归CTE或循环实现;递归CTE必须位于批处理开头,锚成员需明确根节点,必须用UNION ALL连接,OPTION(MAXRECURSION n)须置于最终语句末尾。

不能直接在存储过程中“递归调用自己”,SQL Server不支持存储过程的自调用;你真正需要的是递归查询(CTE)或模拟递归逻辑的函数/循环,而不是字面意义的“递归存储过程”。
WITH 递归 CTE 必须放在存储过程最开头
SQL Server 要求 WITH 必须是批处理或分支内的第一条可执行语句。在存储过程中写递归,常见错误是把它包在 IF 或 BEGIN...END 里,但前面还有别的语句(比如 DECLARE 或空行后跟注释)。
- 错:先
DECLARE @rootId INT = 1;,再WITH Tree AS (...) SELECT ...→ 报错 “Incorrect syntax near 'WITH'” - 对:把整个
WITH块作为IF分支的第一条语句:IF @rootId IS NOT NULL BEGIN WITH Tree AS (...) SELECT * FROM Tree OPTION (MAXRECURSION 500); END - 锚成员必须能明确限定根节点,例如
WHERE id = @rootId,而不是WHERE parent_id IS NULL(除非你要查整棵树)
UNION ALL 是硬性要求,不是风格选择
递归 CTE 的锚成员和递归成员之间必须用 UNION ALL 连接。用 UNION 不会报语法错,但会导致去重——如果两层中存在相同 ID 或 name,第二层数据可能被意外过滤掉,树就断了。
- 递归本质是“逐层追加”,
UNION ALL才符合语义 - 检查执行计划时若看到
Sort算子出现在递归分支里,大概率是误用了UNION - 递归成员中必须引用自身 CTE 名(如
FROM Tree INNER JOIN Dept ON Tree.id = Dept.parent_id),少一个引用就失败
OPTION(MAXRECURSION n) 必须加在最终语句末尾
默认递归深度上限是 100 层。查多级组织架构或评论嵌套时,很容易触发 “The maximum recursion 100 has been exhausted”。这个选项不能写在 CTE 定义里,也不能加在中间 INSERT 或 UPDATE 上,只对紧邻它的 SELECT/INSERT/DELETE 生效。
- 正确:
SELECT * FROM Tree OPTION (MAXRECURSION 500) - 错误:
WITH Tree AS (...) OPTION (MAXRECURSION 500) SELECT * FROM Tree→ 语法错 -
OPTION (MAXRECURSION 0)表示不限制,但生产环境禁用——循环引用或脏数据会让会话卡死、内存暴涨 - 建议把
@maxRecursion设为存储过程输入参数,由调用方按需控制(如“最多展开 6 级部门”)
别写真正的“递归存储过程”,小心无限 EXEC
有人在存储过程末尾手抖加上 EXEC @procName,结果 SQL Server 把它当成了过程体的一部分,导致隐式递归调用。这不是功能,是 bug 行为,会迅速耗尽栈空间并报错 “The statement terminated. The maximum recursion 100 has been exhausted”。
- SQL Server 存储过程没有
RECURSIVE关键字,也不支持CALL self语法 - 所谓“递归存储过程”实际都是误操作或误解,真正可用的只有 CTE 查询、表值函数(如
GetChild(@id))、或 WHILE 循环模拟 - 如果真要跨层级聚合(比如统计每个部门下属员工数),优先用 CTE + 窗口函数,而非嵌套 EXEC
真正容易被忽略的是:CTE 递归不是万能的——它不能动态改变锚条件,也不能在一次查询里同时跑多个不同根节点的树;遇到这类需求,得拆成多个 CTE 块,或改用临时表 + 循环。

















