cte是仅存在于当前sql语句中的命名结果集,不落地、不占磁盘、不进数据字典;核心区别在于可读性与复用性:cte显式命名分步计算,支持多次引用和依赖链定义;mysql需8.0+才支持,递归cte须满足锚成员、递归成员、union all三要素。

CTE 不是临时表,也不是视图,它就是一个带名字的、只活在当前 SQL 语句里的 SELECT 结果集。你写完 WITH 定义后,它不会落地、不占磁盘、不进数据字典,语句一执行完就彻底消失。理解这点,就能避开绝大多数误用。
CTE 和子查询(派生表)到底差在哪
核心区别在于「可读性组织方式」和「复用能力」:
- 子查询必须嵌套在
FROM或WHERE里,多层时逻辑顺序是反的——最内层先执行,但人得从外往里读 - CTE 把“先算什么”显式拎出来命名,比如
dept_avg、recent_orders,主查询直接用名字引用,符合人类思考流 - 同一个 CTE 可以在主查询里被多次
JOIN或UNION,而子查询复制一遍就得改两处,容易漏 - 多个 CTE 可以按依赖顺序定义:
cte1→cte2(引用cte1)→ 主查询(引用cte2),天然支持分步建模
为什么必须用 MySQL 8.0+
因为 CTE 是 SQL 标准中较晚引入的特性,MySQL 直到 8.0 才完整支持,包括非递归和递归两种形式。如果你执行 SELECT VERSION(); 得到的是 5.7.33 这类结果,那所有 WITH 语法都会报错:ERROR 1064 (42000): You have an error in your SQL syntax。别试图降级兼容,没商量余地。
递归 CTE 的三个硬性要求
只要用 WITH RECURSIVE,就必须同时满足:
- 必须有且仅有一个锚成员(anchor),即不引用自身的第一段
SELECT - 必须有且仅有一个递归成员(recursive member),其中必须直接
JOIN或WHERE引用 CTE 自身名(如JOIN org_hierarchy) - 两个成员之间只能用
UNION ALL连接;用UNION会去重,但递归过程依赖行数叠加,去重可能提前截断 - 终止靠
WHERE条件控制(比如WHERE level ),不能依赖空结果“自动停”,否则可能死循环或触达默认 <code>max_recursion_depth=100限制报错
CTE 最容易被当成“高级子查询”来用,但它的真正价值不在语法糖,而在把一段中间计算结果变成可命名、可调试、可组合的逻辑单元——尤其是当你要在同一个语句里反复使用同一组过滤+聚合结果时,少一个 CTE,就多一分维护风险。











