派生表的两种形态
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 为准。
物化的派生表要把中间结果放进临时表,数据量大时既吃内存也吃磁盘。把外层过滤条件写成能被下推的形式,收益比调参数更实在。