CTE中不能直接使用ORDER BY,必须放在最终SELECT中;递归CTE需锚点与UNION ALL连接,且不可含聚合等操作;多CTE按声明顺序可见,不支持跨CTE前向引用;CTE非物化,重复引用会多次执行。
CTE 里不能直接用 ORDER BY,除非配合 TOP 或 OFFSET
很多人写完
就报错,因为标准 SQL 规定:CTE 定义体内不允许出现
,它不保证结果顺序,也不被允许存在——哪怕你只是想“先排好再用”。
真正能生效的排序,得放在最终
里;如果非要在 CTE 内部“控制顺序”,常见做法是加
(SQL Server)或
(通用),但注意:这仅是绕过语法检查,
不保证后续引用时顺序保留
。
SQL Server 可临时用
PostgreSQL / SQL Standard 必须把
移到最外层
CTE 不是视图,它不缓存排序状态,多次引用同一 CTE 时顺序可能不一致
递归 CTE 必须包含锚点 + 递归成员,且 UNION ALL 是硬性要求
写
(PostgreSQL)或普通
(SQL Server)做树形查询时,漏掉锚点查询、或者用了
而不是
,都会直接报错或无限循环。
锚点是起点(比如所有根节点),递归成员必须引用自身 CTE 名,并且只能出现在
或
的右侧。SQL Server 还要求显式设置
以防爆栈。
锚点和递归部分之间必须用
连接,
会去重并破坏递归逻辑
递归成员中不能出现聚合、
、
等不支持的操作
SQL Server 默认最大递归深度是 100,超限报错
,需加
多个 CTE 共享作用域,但不能跨 CTE 引用未定义的别名
是合法的,但反过来——比如在
里引用
,就会报
。CTE 按声明顺序依次可见,后声明的可以前向引用,但前声明的看不到后面的。
另外,CTE 名称只在当前语句有效,下一条
就失效;也不能在存储过程中跨语句复用,除非重新定义。
CTE 列表中靠前的项无法引用靠后的项,哪怕看起来“逻辑上应该能”
嵌套子查询里不能直接用外层 CTE,必须把 CTE 拉平或改用临时表
名字冲突时,CTE 优先级高于同名表,容易导致
却查不到原因
CTE 性能不等于视图或子查询,过度嵌套可能拖慢执行计划
CTE 是逻辑结构,不是物化结果。数据库优化器通常会把它内联展开,等价于巨型子查询。这意味着:它不会自动缓存中间结果,也不会减少重复计算——如果同一个 CTE 被引用三次,很可能执行三次底层查询。
某些场景下(如大表关联+过滤后反复使用),用
或
反而更快;PostgreSQL 9.3+ 支持
(需显式指定),但 SQL Server 和 MySQL 目前都不原生支持物化 CTE。
用
或执行计划看 CTE 是否真的被展开,有没有重复扫描
避免在 CTE 里写
后又只取几列,字段越多,内存和网络开销越大
MySQL 8.0+ 对 CTE 优化较弱,复杂嵌套建议拆成临时表验证性能
事情说清了就结束。CTE 看似简单,但顺序、递归规则、作用域和执行模型这几处,最容易在调试时卡住半天。
WITH cte AS (SELECT * FROM users ORDER BY created_at)ORDER BYSELECTTOP (100) PERCENTOFFSET 0 ROWSSELECT TOP (100) PERCENT * FROM users ORDER BY created_atORDER BYSELECTWITH RECURSIVEWITHUNIONUNION ALLFROMJOINMAXRECURSIONUNION ALLUNIONGROUP BYOUTER JOINMsg 530OPTION (MAXRECURSION n)WITH a AS (...), b AS (...), c AS (SELECT * FROM a JOIN b ...)abInvalid object name 'b'SELECTcolumn not foundSELECT ... INTO #tempCREATE TABLE #tMATERIALIZEDEXPLAINSELECT *