首页/数据/SQL 速查/派生表合并与物化(derived_merge)
性能与执行计划72 条

派生表合并与物化(derived_merge)

MySQL 5.7+ 会把 FROM 子查询合并进外层查询(受阻时物化成临时表):用 EXPLAIN 辨别两种形态。

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

派生表的两种形态

MySQL 5.7 起默认把 FROM 子句里的派生表(子查询)合并进外层查询,让外层条件能下推到子查询内部;当子查询带聚合、DISTINCT、GROUP BY、LIMIT 等结构时无法合并,只能物化成临时表。走的是哪一种,用 EXPLAIN 一眼就能看出来。

基本信息

合并条件无聚合、DISTINCT、GROUP BY、LIMIT、UNION、窗口函数等阻断结构
开关optimizer_switch 中 derived_merge 默认开启,可会话级关闭做对比
物化表现EXPLAIN 里出现派生表节点与临时表
8.0.22+物化的派生表也支持条件下推,中间结果更小
提示可用 NO_MERGE 提示强制不合并

典型使用场景

  • 判断子查询到底被合并还是物化
  • 排查「加了外层条件反而更慢」的问题
  • 对比派生表与直接连接的写法差异
  • 验证版本升级后执行计划的变化
  • 用提示做对照实验确认瓶颈

示例

示例
-- 合并形态:外层条件下推到派生表内部
EXPLAIN SELECT *
FROM (SELECT * FROM orders WHERE user_id = 100) t
WHERE t.status = 'paid';

-- 对照:用提示强制不合并,观察物化形态
EXPLAIN SELECT /*+ NO_MERGE(t) */ *
FROM (SELECT * FROM orders WHERE user_id = 100) t
WHERE t.status = 'paid';

-- 带聚合的派生表无法合并,只能物化
EXPLAIN SELECT *
FROM (SELECT user_id, COUNT(*) AS c FROM orders GROUP BY user_id) t
WHERE t.c > 10;

两种形态对照

形态EXPLAIN 表现性能特点
合并计划里只有外层表与下推后的条件条件下推,通常更优
物化出现派生表节点与临时表多一次中间结果落盘或内存表
物化加条件下推临时表上仍带过滤条件中间结果更小,8.0.22+ 支持
关闭 derived_merge强制物化用于对比验证
派生表是否合并取决于子查询结构,不能凭语句形态判断:同一个子查询加上 GROUP BY 或 LIMIT 之后行为就完全不同,以 EXPLAIN 为准。
物化的派生表要把中间结果放进临时表,数据量大时既吃内存也吃磁盘。把外层过滤条件写成能被下推的形式,收益比调参数更实在。

易错点

  • 把派生表当必然物化:默认会合并并把条件下推,判断依据应是 EXPLAIN 输出
  • 对聚合类子查询期待合并:聚合会阻断合并只能物化,需要从子查询设计上减少中间结果
  • 外层条件写在 WHERE 却指望一定下推:部分条件无法下推,需要改写 SQL 才能生效
  • 忽略版本差异:8.0.22 起物化派生表也支持条件下推,跨版本对比计划时要留意
  • 用提示长期关闭合并:NO_MERGE 只适合做对照实验,长期使用会让计划失去灵活性

数据来源与参考

  • MySQL 8.0 Reference Manual · Derived Tables
  • MySQL 8.0 Reference Manual · Optimizer Hints
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌