应先评估必要性:仅当sql超5个join、嵌套超2层或explain显示重复计算时才拆分;三表强过滤inner join强行拆反增开销,且需警惕using temporary、type=all等执行计划信号。

拆分前先确认是否真有必要
超过5个表的JOIN、嵌套子查询超2层、或EXPLAIN显示某部分被重复计算多次,才值得拆。不是所有复杂SQL都该动——比如三表INNER JOIN且每张表都有强过滤条件,强行拆反而增加网络和应用层开销。
常见错误现象:EXPLAIN里出现Using temporary; Using filesort、type=ALL、或同一张表在执行计划中出现两次以上,基本就是拆分信号。
- 用
EXPLAIN FORMAT=TREE(MySQL 8.0+)或EXPLAIN ANALYZE(PostgreSQL)看实际执行路径,别只信rows估算值 - 检查
filtered列:低于10%说明WHERE条件没起效,得先优化过滤逻辑,再考虑拆 - 如果主键ID能覆盖最终结果集(比如只要查1000条订单详情),优先走
IN (id_list)分批拉取,比临时表更轻量
临时表建法:字段精简 + 索引对齐访问模式
临时表不是原表拷贝,字段越少越好。带VARCHAR(200)或TEXT字段进去,体积可能翻倍,扫描变慢。
索引必须按后续JOIN顺序建,且优先覆盖ON等值字段:
- 如果下一步是
JOIN tmp_t3 ON t1.b = tmp_t3.b,至少要有INDEX(b) - 若还有
WHERE tmp_t3.status = 'active',建INDEX(b, status),不是INDEX(status, b) - 主键别默认设
id——除非它真被用于下一轮JOIN;用PRIMARY KEY (b)更贴合访问模式 -
CREATE TEMPORARY TABLE tmp AS SELECT ...一步到位,比先CREATE再INSERT少出错
CTE vs 临时表:选哪个看复用频率和数据库版本
CTE适合单次使用、逻辑清晰的场景,但MySQL 5.7不支持,别踩坑。
临时表适合需要多次引用、或中间结果要加索引/统计/调试的场景。
- CTE写法紧凑,但每次执行都会重算——如果同一个CTE在查询里被引用两次,它就跑两遍
- 临时表可显式
ALTER TABLE ... ADD INDEX,CTE没法加索引 - CTE名称本身就是文档,比如
WITH recent_orders AS (...)比嵌套子查询里的(SELECT ...)易懂得多 - 跨会话复用?不行。临时表只在当前会话可见,
DROP TEMPORARY TABLE不是必须,但显式清理更安全
应用层组装时最容易漏掉的三件事
拆成多条SQL后,性能未必提升,反而可能因忽略边界情况引入bug。
- ID列表不能直接塞子查询:
WHERE id IN (SELECT ...)在MySQL 5.7+仍不稳定,务必先查出ID数组再拼IN (1,2,3...) - LEFT JOIN变成多次查询后,NULL语义容易丢:比如原SQL中
LEFT JOIN addresses返回NULL地址,拆开后得用代码补空对象,否则前端报错 - 事务一致性靠应用层兜底:多个查询之间若有状态依赖(如库存扣减),得用分布式锁或数据库事务包住,不能假定“查完再查”天然一致
最常被忽略的是字段类型隐式转换——比如t1.user_id INT和t2.uid VARCHAR在JOIN时触发全表转换,拆开后每条SQL都得单独校验SHOW CREATE TABLE。










