cte在sql server中必须作为批处理或分支内首条语句,前置语句需加分号;递归cte须满足锚成员在左、递归成员在右且显式引用自身、末尾加option(maxrecursion n)三条件;外层谓词须下推至各分支,列类型需显式统一;聚合统计需在外层group by,路径拼接需cast防截断。

必须用 WITH + UNION ALL 结构,WITH 必须是分支内首条语句,且递归深度超100层会直接中断——不是慢,是报错退出。
CTE在存储过程里写在哪?位置错了就语法失败
SQL Server 要求 WITH 必须是批处理(或 IF/BEGIN...END 分支)中的第一个可执行关键字。常见错误是把它塞在 SELECT 或 SET 后面:
- 错:在
IF @id IS NOT NULL BEGIN SELECT 1; WITH Tree AS (...) ... END——WITH前有SELECT,报Incorrect syntax near 'WITH' - 对:把整个
WITH块提前到分支开头,例如IF @id IS NOT NULL BEGIN WITH Tree AS (...) SELECT * FROM Tree OPTION (MAXRECURSION 500); END - 所有前置语句(包括
DECLARE、SET、PRINT)末尾必须加分号,否则上一条语句可能“吞掉”WITH
递归CTE必须满足的三个硬约束
缺一不可,否则要么编译失败,要么运行时卡死或结果错乱:
- 锚成员(anchor)必须在
UNION ALL左侧,且不能引用自身 CTE 名;典型写法是WHERE ParentID IS NULL或WHERE ID = @rootId - 递归成员(recursive term)必须在
UNION ALL右侧,且显式引用自身 CTE 名,例如INNER JOIN TreeCTE t ON c.ParentID = t.ID - 必须加
OPTION (MAXRECURSION n)在最终SELECT末尾,n建议设为明确上限(如500),禁用0;它不能写在 CTE 定义里,也不能放在INSERT或UPDATE中间
为什么查着查着就没了?谓词没下推+类型不一致
CTE 不是临时表,它不物化中间结果,优化器可能把外层 WHERE 全部延迟到最后一刻执行,导致全表扫描:
- 别在外层
WHERE过滤租户或状态,例如WHERE tenant_id = @tenant_id,而要在每个 CTE 子查询里都加上——尤其锚成员和递归成员都要写 -
UNION ALL各分支列数、顺序、类型必须严格一致;隐式转换(如VARCHAR和NVARCHAR)会导致列对齐失败,报all queries in a UNION must have the same number of columns - 显式用
CAST统一类型,列名用AS显式声明,别依赖数据库自动推导
查完树形数据后怎么聚合?CTE本身不支持直接套SUM
递归 CTE 展开的是平表,想按层级统计、算子节点数量、汇总金额,必须在外层再套一层 GROUP BY:
- 例如要统计每级部门人数:
SELECT Level, COUNT(*) FROM TreeCTE GROUP BY Level,不能在 CTE 内写COUNT(*) OVER (PARTITION BY Level) - 路径拼接要用
CAST++,比如CAST('/' + Name AS VARCHAR(500)) AS Path,避免隐式截断 - 如果递归结果要多次引用(如既要查子树又要查路径),考虑改用
#temp表并建索引——CTE 没统计信息,也没办法加索引
最易被忽略的是:CTE 的“逻辑拆分”只是给优化器多一次重写机会,实际是否物化、走不走索引、有没有 Table Spool,得看执行计划里的实际算子,不是写了 WITH 就自动快。











