首页/数据/SQL 速查/CTE 物化与内联的选择

CTE 物化与内联的选择

CTE 是内联展开还是物化成临时结果会影响性能:PostgreSQL 12 起默认按引用情况决定,MySQL 由优化器自行决定。

SQL 速查数据更新于 2026-10-13

物化还是内联

CTE(WITH 子句)到底是内联展开还是物化成临时结果,直接影响性能:物化只算一次但会挡住外层条件下推,内联则可能被重复计算。PostgreSQL 12 之前 CTE 一律物化,12 起改为按引用情况决定,并允许用 MATERIALIZED / NOT MATERIALIZED 显式指定;MySQL 没有这两个关键字,由优化器自行决定合并还是物化。

基本信息

PostgreSQL 12 之前CTE 总是物化,等价于加了一层优化屏障
PostgreSQL 12+默认按引用次数决定,可用 MATERIALIZED / NOT MATERIALIZED 控制
MySQL无显式关键字,优化器决定合并或物化
判断方法看 EXPLAIN 里是否出现物化节点或临时表
取舍原则重量级结果被多次引用用物化,小结果集想下推用内联

典型使用场景

  • 一个聚合结果被多处引用,希望只计算一次
  • 小结果集想和外层条件一起下推,希望内联展开
  • 递归查询与报表口径的复用
  • 排查「同样的 SQL 在 PG 12 前后性能变差」
  • 跨库迁移时确认 CTE 行为差异

PostgreSQL 显式控制

PostgreSQL 显式控制
-- 显式物化:大聚合只算一次,后续引用都复用它
WITH heavy AS MATERIALIZED (
    SELECT user_id, COUNT(*) AS cnt
    FROM logs
    GROUP BY user_id
)
SELECT * FROM heavy WHERE cnt > 100;

-- 显式内联:小表允许被外层条件下推、被多次展开
WITH light AS NOT MATERIALIZED (
    SELECT * FROM small_table WHERE flag = 1
)
SELECT * FROM light l JOIN big b ON b.k = l.k;

从执行计划确认

从执行计划确认
-- PostgreSQL:看是否出现 CTE Scan 或 Materialize 节点
EXPLAIN (ANALYZE, COSTS OFF)
WITH t AS (SELECT * FROM orders WHERE user_id = 1)
SELECT * FROM t JOIN users u ON u.id = t.user_id;

-- MySQL:看是否出现派生表或临时表节点
EXPLAIN FORMAT=TREE
WITH t AS (SELECT * FROM orders WHERE user_id = 1)
SELECT * FROM t JOIN users u ON u.id = t.user_id;

物化与内联的取舍

场景物化更合适内联更合适
被引用多次的重量级查询是,只算一次否,会重复计算
小结果集加外层过滤否,条件下推被挡住是,条件下推进 CTE
含聚合或窗口的 CTE通常只能物化一般不可合并
含 LIMIT 或 DISTINCT 的 CTE常被物化视优化器判断
把 CTE 当「只算一次的缓存」是常见误解:PostgreSQL 12+ 可能把它内联导致重复计算,MySQL 的合并策略也由优化器决定。想要只算一次,就显式物化或改用临时表。
数据库大版本升级会改变 CTE 默认行为(例如 PG 11 升 12),升级前把关键查询的执行计划留档,升级后对比,避免性能回退时难以定位原因。

易错点

  • 把 CTE 当只算一次的缓存:PG 12+ 可能内联导致重复计算,重量级聚合要显式写 MATERIALIZED
  • 希望外层条件能下推进 CTE:物化会挡住条件下推,PG 可写 NOT MATERIALIZED,MySQL 只能改写语句结构
  • 升级数据库后不重新对比计划:CTE 默认行为变了,性能回退却以为是数据增长
  • 在 MySQL 里写 MATERIALIZED / NOT MATERIALIZED:没有这两个关键字会语法报错,跨库迁移要逐条改写
  • 每层逻辑都拆一个 CTE:可供优化器合并的空间被压小,计划里堆满临时表节点,性能反而下降

数据来源与参考

  • PostgreSQL Documentation · WITH Queries · Materialization
  • MySQL 8.0 Reference Manual · Optimizing WITH
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌