cte中不能直接使用order by,必须放在最终select中;递归cte需锚点与union all连接,且不可含聚合等操作;多cte按声明顺序可见,不支持跨cte前向引用;cte非物化,重复引用会多次执行。

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









