当前位置:首页 > 文章列表 > 数据库 > MySQL > MySQL CTE 什么时候会物化而不是合并

MySQL CTE 什么时候会物化而不是合并

来源:17golang原创 2026-10-04 09:17:48 0浏览 收藏

MySQL CTE(公共表表达式)并不是写进 WITH 就一定先算出一张临时表。优化器通常会在“把 CTE 合并进外层查询”和“把 CTE 物化为内部临时表”之间选择:能合并时,外层条件更容易下推;不能合并或明确要求保留边界时,才会走物化。判断重点不是 CTE 的名字,而是它的查询结构、引用方式和整个执行计划。

官方地址:https://dev.mysql.com/doc/refman/8.4/en/derived-table-optimization.html

要点速览
  • 普通、非递归且结构简单的 CTE 通常具备合并条件,但最终仍由优化器按代价选择。
  • 聚合、窗口函数、DISTINCT、GROUP BY、HAVING、LIMIT、UNION 等结构会阻止合并;递归 CTE 始终物化。
  • 用 EXPLAIN 看独立物化节点,必要时使用 MERGE()、NO_MERGE() 或检查 derived_merge,不要只凭 SQL 外观猜测。

先看清 MySQL CTE 的两种处理方式

合并可以理解为把 CTE 查询块展开到外层。比如只做字段投影和过滤的 recent_orders,优化器可能把它和外层的订单表一起重排,让外层条件参与索引访问。物化则把 CTE 结果作为内部临时表,后续查询把它当作一个独立数据源读取。

-- 这个 CTE 只有投影和过滤,具备被合并的基本条件
WITH recent_orders AS (
    SELECT order_id, customer_id, created_at
    FROM orders
    WHERE created_at >= '2026-01-01'
)
SELECT customer_id, COUNT(*) AS order_count
FROM recent_orders
GROUP BY customer_id;

合并不是“少了一步固定执行”,而是给优化器更多重写空间;物化也不等于一定更慢。物化会被延迟到真正需要结果时才进行,某些场景还可能为内部结果自动建立索引。多次引用同一个已经物化的 CTE 时,MySQL 会在本次查询中复用它,而不是为每个引用完整计算一遍。

MySQL CTE 合并与物化的查询结构说明图,展示外层查询、CTE 查询块、条件下推和内部临时表边界
图1:MySQL CTE 合并与物化的结构说明图,展示查询块、条件下推和内部临时表之间的关系;这是静态说明图,不是运行截图。

这些 CTE 结构会让合并失去条件

下面这些结构会阻止派生表、视图引用和 CTE 合并到外层查询块:聚合函数或窗口函数、DISTINCT、GROUP BY、HAVING、LIMIT、UNION 或 UNION ALL、选择列表中的子查询、用户变量赋值,以及只引用字面量而不读取底层表的查询。

-- 聚合和分组形成结果边界,外层查询不能把它当作普通表直接展开
WITH customer_totals AS (
    SELECT customer_id, SUM(amount) AS total_amount
    FROM orders
    GROUP BY customer_id
)
SELECT customer_id, total_amount
FROM customer_totals
WHERE total_amount > 10000;

-- 递归 CTE 是另一类边界:MySQL 对它始终采用物化策略
WITH RECURSIVE tree AS (
    SELECT id, parent_id, 0 AS depth
    FROM category
    WHERE parent_id IS NULL
    UNION ALL
    SELECT c.id, c.parent_id, tree.depth + 1
    FROM category AS c
    JOIN tree ON c.parent_id = tree.id
)
SELECT id, depth FROM tree;

还有一个容易忽略的上限:如果合并后外层查询块会引用超过 61 张基表,优化器会选择物化。这个判断属于计划结构限制,不是“CTE 超过多少行就物化”,也不能用行数阈值替代。

MySQL CTE 物化边界说明图,展示聚合、窗口函数、DISTINCT、递归和 UNION 对查询块的限制
图2:MySQL CTE 物化边界结构图,标出聚合、窗口函数、DISTINCT、递归和 UNION 等会保留查询块边界的实体;这是静态说明图,不是运行证据。

用 EXPLAIN、提示和开关确认计划

不要只因为 SQL 使用了 WITH 就断定它已经物化。先执行 EXPLAIN,再观察 CTE 是否作为独立的物化来源出现,以及外层谓词是否能参与底层表访问。不同格式的 EXPLAIN 展示细节不同,排查时应以实际计划为准。

-- 先保留原查询,查看优化器是否展开 CTE 或保留独立来源
EXPLAIN FORMAT=TREE
WITH recent_orders AS (
    SELECT order_id, customer_id
    FROM orders
    WHERE created_at >= '2026-01-01'
)
SELECT r.customer_id
FROM recent_orders AS r
WHERE r.order_id > 100000;

-- 仅对当前语句施加倾向,避免把全局开关当成长期修复
WITH recent_orders AS (
    SELECT order_id, customer_id FROM orders
)
SELECT /*+ NO_MERGE(recent_orders) */ customer_id
FROM recent_orders;

-- 检查是否允许派生表、视图和 CTE 采用合并策略
SELECT @@optimizer_switch;

MERGE(cte_name) 和 NO_MERGE(cte_name) 只在其他规则允许时发挥作用;如果 CTE 本身含有聚合或递归结构,提示不能把不合法的合并变成合法。全局关闭 optimizer_switch 中的 derived_merge 会影响更多语句,生产环境应先用单语句提示或灰度计划验证。

按这个清单决定是否干预

检查点看到的现象处理建议
CTE 结构只投影、过滤,且非递归先接受优化器合并,再看 EXPLAIN
结果边界聚合、窗口、DISTINCT、LIMIT、UNION按物化查询块分析,关注临时结果访问
引用方式同一 CTE 被多处引用确认是否一次物化、多次复用,并查看自动索引
计划异常合并后连接顺序或条件下推不理想用 NO_MERGE 做对照计划,不要直接全局关闭

实际优化时,先比较两份计划,再结合扫描行数、连接顺序和过滤位置判断代价。物化提供了清晰的结果边界,但可能产生临时表读写;合并减少了边界,却可能让外层查询变得复杂。最终目标是让访问路径匹配数据分布,而不是追求“所有 CTE 都合并”或“所有 CTE 都物化”。

常见问题

CTE 写了 GROUP BY 就一定会物化吗?

它会失去合并条件,通常按物化查询块处理;仍应通过 EXPLAIN 确认实际计划,而不是只看 SQL 文本。

多次引用 CTE 会重复计算吗?

如果该 CTE 被物化,本次查询只物化一次,多个引用可以复用;优化器还可能针对不同引用建立合适的内部索引。

NO_MERGE 能解决所有性能问题吗?

不能。它只是给当前 CTE 增加一个计划方向,仍需对比执行计划、临时结果规模和连接访问代价。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
shizuku User Service是什么?独立进程、UID与Android API边界说明shizuku User Service是什么?独立进程、UID与Android API边界说明
上一篇
shizuku User Service是什么?独立进程、UID与Android API边界说明
Go tls.Config.Clone 怎么安全派生连接配置
下一篇
Go tls.Config.Clone 怎么安全派生连接配置
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    325次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    382次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    376次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    340次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    167次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码