怎样在SQL中对比CTE与临时表及嵌套子查询的开销

落萱吖_8573

落萱吖_8573

2026-10-06

735人浏览

原创

cte 的“简洁”是表象,临时表才是性能更稳的选择;真实开销需看执行计划:mysql 查 materialized、postgresql 看 cte scan 次数、sql server 找重复 nodeid 或 spool;超10万行或需多次 join/过滤/排序时,应优先建带索引的临时表。

怎样在sql中对比cte与临时表及嵌套子查询的开销

看执行计划,别信“语法看起来更干净”

CTE 写起来像分步说明书,但 MySQL 8.0.31 之前默认不物化,WITH cte AS (SELECT ...) 被多次引用时,可能真被算两次;PostgreSQL 默认物化,但若执行计划里出现重复的 CTE Scan 节点,说明没生效;而子查询在 WHERE 里套三层 (SELECT ...),执行计划里大概率是 DEPENDENT SUBQUERY 或 MATERIALIZED 标记混乱——这些才是真实开销信号。

实操建议:

  • MySQL 中用 EXPLAIN FORMAT=TREE 查看是否含 MATERIALIZED;没加 MATERIALIZED 提示就别假设它缓存了
  • PostgreSQL 中用 EXPLAIN (ANALYZE, BUFFERS),重点看 CTE Scan 出现几次、Shared Hit 是否高
  • SQL Server 里查执行计划 XML,找 RelOp NodeId 是否重复,或看是否有 Spool 算子
  • 别只看“rows”估算值——临时表的统计信息更准,大表上 #tmp 的 Actual Rows 往往比 CTE 的 Estimate Rows 可靠得多

数据量 > 10 万行且要 JOIN 多次时,临时表不是更重,是更稳

CTE 在跨 JOIN 场景下容易让优化器误判:比如 WITH a AS (SELECT id, SUM(val) FROM big_table GROUP BY id),再 JOIN a ON ... JOIN a ON ...,MySQL 可能对每个 JOIN 都重跑聚合;而临时表建完立刻有准确行数和分布,CREATE INDEX 后还能加速关联。

实操建议:

  • 中间结果超 10 万行,且后续要 JOIN、WHERE 过滤、ORDER BY 排序,直接上 CREATE TEMPORARY TABLE tmp_xxx AS ...
  • 建完立刻 CREATE INDEX,尤其对 JOIN 列或 WHERE 条件列
  • 避免在存储过程中反复 DROP + CREATE TEMPORARY TABLE,一次建好复用全程
  • 命名加唯一后缀,如 tmp_user_agg_20260929_12345,防连接池中残留冲突

相关子查询是性能黑洞,优先拆成 CTE 或临时表

像 WHERE t1.id IN (SELECT t2.ref_id FROM t2 WHERE t2.status = t1.status) 这种,t1 每行都触发一次 t2 扫描,O(n²) 不是吓唬人。这时候 CTE 至少能强制先算一遍 t2 结果集;临时表还能加索引加速匹配。

实操建议:

  • 把相关子查询里的内层逻辑抽出来,定义为 CTE,主查询改用 JOIN 替代 IN 或 EXISTS
  • 如果 CTE 抽出来后仍慢(比如 t2 行数太大),立刻转 CREATE TEMPORARY TABLE 并对 ref_id 和 status 建联合索引
  • 别试图用 /*+ MATERIALIZE() */ 强撑——MySQL 8.0.31+ 才支持,老版本无效
  • SQL Server 上可试 OPTION (RECOMPILE),但不如物理临时表稳定

递归和多步加工是分水岭,别硬套 CTE

CTE 真正不可替代的只有两个场景:递归(组织树、路径展开)和明确依赖链(A ← B ← C)。除此之外,只要涉及“INSERT → UPDATE → SELECT”三步走,或者中间结果要加索引、要统计采样、要人工校验,CTE 就不是选项,是障碍。

实操建议:

  • 需要递归?必须用 WITH RECURSIVE,子查询和临时表都搞不定
  • 要对中间结果做多次不同过滤?CTE 不行,临时表可以 SELECT * FROM #tmp WHERE ...、UPDATE #tmp SET ... 自由操作
  • 想调试中间数据?CTE 查不了,临时表直接 SELECT TOP 100 * FROM #tmp
  • 跨语句复用?CTE 生命周期只到当前 SELECT 结束,临时表活到会话结束

CTE 的“简洁”是给眼睛看的,临时表的“啰嗦”是给优化器听的。真正卡住性能的,往往不是语法选择本身,而是你没看清哪一步该固化、哪一步该放开让优化器推导。

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4023

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

851

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1049

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

5881

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

2803

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

5860

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

7801

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1070

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

932

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.4万人学习