标量子查询在WHERE中可能被重复执行,性能差距取决于嵌套层级、数据量等;出现DEPENDENT SUBQUERY且外层行数>1000即为瓶颈;临时表更可控但有开销;CTE物化行为因引擎而异,需显式提示才能避免重复计算。

差距大,但不是固定值——取决于嵌套层级、数据量、引擎版本和是否被优化器物化。标量子查询在 WHERE 里被反复执行时,性能可能比临时表差一个数量级;而简单的一层 FROM 子查询,和临时表几乎没差别。
标量子查询在 WHERE 中被重复执行?先看执行计划
MySQL 或 PostgreSQL 里,(SELECT COUNT(*) FROM orders WHERE user_id = u.id) 这类写法,如果外层扫描 10 万行,它真可能被执行 10 万次——不是“一次算完再比对”,而是每行都触发一次子查询调用。
- 用
EXPLAIN ANALYZE(PostgreSQL)或EXPLAIN FORMAT=TREE(MySQL 8.0+)确认是否标记为DEPENDENT SUBQUERY - 只要出现这个字样,且外层行数 > 1000,基本就是瓶颈所在
- 别信“优化器会自动物化”——MySQL 5.7+ 对单层标量子查询会尝试,但一旦带
GROUP BY、ORDER BY或字段没索引,立刻退化
临时表不是一定更快,但更可控
显式建临时表(CREATE TEMPORARY TABLE tmp AS ...)把中间结果固化下来,后续引用不重算,但开销也真实存在:MySQL 默认用 MEMORY 引擎,可一旦字段含 TEXT 或长度超限,自动降级到磁盘 MyISAM,IO 突然飙升;PostgreSQL 的临时表默认走本地磁盘,没内存优势。
- 别在存储过程中反复
DROP + CREATE TEMPORARY TABLE——每次触发元数据锁和文件系统操作,比子查询还慢 - 命名务必加唯一前缀,比如
tmp_order_20260701_98765,硬编码tmp或t1在连接池场景下极易冲突 - 长连接或连接池(如
mysql-connector-python的pool_size)中,临时表不会随脚本结束自动销毁,可能残留数小时
CTE 不等于临时表,物化行为因引擎而异
很多人以为 WITH 是“高级临时表”,其实 MySQL 默认非物化(每次引用都重算),PostgreSQL 默认物化(但可手动关)。同样一段逻辑,在两个引擎上执行计划可能完全不同。
- MySQL 8.0.23+ 支持
/*+ MATERIALIZE() */提示,但必须显式加,否则仍是逻辑视图 - PostgreSQL 中若
EXPLAIN显示 CTE 节点下有重复子树,说明没物化,得改用WITH RECURSIVE ... AS MATERIALIZED - 别为了语法简洁硬套 CTE——尤其当内部含
LIMIT、ORDER BY,且外层还要JOIN时,优化器常放弃条件下推,反而更慢
真正影响性能的从来不是“子查询 or 临时表”这个二选一,而是你有没有让优化器看清你的意图。执行计划里一旦出现 Using temporary; Using filesort,说明 MySQL 已经被迫建临时表并排序——这时候再纠结语法,不如先加索引、拆 JOIN、或者把关键中间结果显式落地。


















