SQL Server不支持WITH RECURSIVE语法,必须用WITH自引用CTE实现递归;锚成员须在UNION ALL左侧、递归成员在右侧,列数顺序类型严格一致,且需显式指定OPTION (MAXRECURSION n)防止超100层报错。

SQL Server 里用 WITH RECURSIVE?别踩这个坑
SQL Server 不支持 WITH RECURSIVE 语法(那是 PostgreSQL/MySQL 8.0+ 的写法),必须用 WITH + 自引用 CTE 实现递归。直接照搬其他数据库的递归写法会报错:Incorrect syntax near the keyword 'RECURSIVE'。
关键点在于:CTE 定义里不能写 RECURSIVE,且锚成员和递归成员必须用 UNION ALL 连接,类型严格一致(列数、顺序、数据类型)。
- 锚成员查询必须能独立执行,返回最顶层节点(比如
parent_id IS NULL或level = 1) - 递归成员中引用 CTE 名称时,必须是“自身”,不能加 schema 前缀(如
dbo.tree_cte会报错) - 必须有终止条件——靠
MAXRECURSION提示或逻辑上父级不再产生子级来控制,否则可能死循环
拼接路径时 CONCAT 和 + 字符串连接的区别
在 SQL Server 中,用 + 拼接时只要任意一端为 NULL,整个结果就是 NULL;而 CONCAT 会把 NULL 当空字符串处理,更安全。
构建路径时,父级路径可能是 NULL(顶层节点),所以推荐用 CONCAT:
CONCAT(parent.path, '/', t.name)
而不是:
parent.path + '/' + t.name -- 一旦 parent.path 为 NULL,整列变 NULL
- 如果兼容老版本 SQL Server(CONCAT 不可用,改用
ISNULL(parent.path, '') + '/' + t.name - 注意路径分隔符统一:用
'/'还是'\'取决于业务场景,但视图里最好固定,避免后续解析混乱 - 路径开头是否加根标识(如
'/root')需在锚成员里显式写死,递归成员只负责追加
创建可复用的层级路径视图要避开的三个硬伤
直接把递归 CTE 写进视图定义是可行的,但容易出问题:
-
ORDER BY不能出现在视图定义中(除非配合TOP或OFFSET/FETCH),想按路径排序得在查询视图时加 - 递归深度默认上限是 100,深层树(如组织架构 >100 级)会报错
The statement terminated. The maximum recursion 100 has been exhausted,需在视图外加OPTION (MAXRECURSION 0)(不建议设 0,应设合理上限如 500) - 视图里不能带参数,所以“从某节点开始查子树”这种需求,无法在视图内实现,得用表值函数(TVF)替代
典型安全写法是:视图只提供完整树 + 路径字段,筛选逻辑交给上层查询。
PostgreSQL/MySQL 用户迁移到 SQL Server 时的兼容断点
PostgreSQL 的 WITH RECURSIVE 和 MySQL 的 WITH RECURSIVE cte AS (...) 在语法结构上更线性,而 SQL Server 的 CTE 递归要求更严:
- 锚成员和递归成员的列名必须完全一致(不能靠
AS重命名后自动对齐),建议显式写出所有列名 - SQL Server 不支持在递归成员中使用聚合、窗口函数、
GROUP BY—— 想算每层节点数?得递归完再外面套一层GROUP BY -
CYCLE检测(防环)是 PostgreSQL 特性,SQL Server 没有原生支持,需靠业务约束或额外字段(如path LIKE '%/X/Y/X/%')手动检测
路径拼接本身不难,难的是让递归稳定跑满真实业务深度,同时不被 NULL、类型隐式转换、权限隔离这些细节卡住。先确保单条记录能跑通,再压测 10 层以上路径生成性能。

















