UNION ALL 能提升 7–11 倍性能,前提是数据量超 10 万行且业务允许重复;它硬性跳过 Using temporary 和 Using filesort,只要无外层 ORDER BY,存储过程与普通查询行为一致。

能提升 7–11 倍,但前提是数据量过 10 万行且无需去重;小数据量差异不明显,而该提速完全来自跳过 Using temporary 和 Using filesort。
UNION ALL 在存储过程里真能跳过排序和临时表?
能,而且是硬性跳过——只要没显式写 ORDER BY,MySQL 就不会在执行计划里生成 Using temporary 或 Using filesort。这点在存储过程中和普通查询完全一致,不是“可能优化”,而是机制决定:
- UNION 强制走两步:先
UNION ALL拼出全部结果 → 再套一层SELECT DISTINCT+ 隐式ORDER BY - UNION ALL 就是顺序输出:第一个
SELECT开始返回,数据立刻往下流;它结束,第二个紧跟着开始,中间不建任何结构 - 存储过程里用
INSERT INTO ... SELECT ... UNION ALL ...或直接SELECT ... UNION ALL ...返回结果集,都享受同等流式行为
实测性能差距到底多大?看数据量说话
在 MySQL 8.0 + InnoDB 环境下,两个各返回 50 万行的子查询合并(如分表日志汇总),实测耗时如下:
- 10 万行总结果:UNION ALL ≈ 82ms,UNION ≈ 620ms(7.5 倍)
- 50 万行:UNION ALL ≈ 395ms,UNION ≈ 3850ms(9.7 倍)
- 100 万行:UNION ALL ≈ 810ms,UNION ≈ 9200ms(11 倍以上)
注意:这个差距不是线性增长,而是随数据量陡增。原因在于 UNION 的去重排序是 O(n log n) 复杂度,内存不够时还会刷磁盘——存储过程中若频繁调用,tmp_table_size 被打满会导致大量 disk-based temporary table,I/O 成瓶颈。
为什么在存储过程里更容易踩坑?
因为逻辑封装后,开发者容易忽略两点:
-
ORDER BY写错位置:子查询里加ORDER BY create_time DESC完全无效,必须写在外层;但一旦加了外层ORDER BY,UNION ALL 也会触发排序,性能优势瞬间归零 - 误判“天然无重复”:比如用
UNION ALL合并user_shard1.users和user_shard2.users,但如果某业务路径允许跨库注册,ID 并非全局唯一,后续去重逻辑就漏了——这时换回 UNION 不是退步,而是补救 - NULL 值隐性重复:UNION 会把多个
NULL当作相同值去重,UNION ALL 全部保留;如果存储过程输出被上层当维度值用,NULL 行数突增可能引发前端渲染异常
列对齐和类型检查,存储过程里一样严格
别以为封装进存储过程就能绕过校验。以下任一情况都会让 CREATE PROCEDURE 直接报错,和用不用 UNION 无关:
- 子查询列数不等:
SELECT id FROM t1和SELECT id, name FROM t2 - 对应列类型冲突:第一条返回
NOT NULL VARCHAR(50),第二条返回NULL,MySQL 8.0+ 严格模式下直接拒绝 - 字段名不一致但没显式对齐:
SELECT uid FROM a和SELECT user_id FROM b→ 结果列名是uid,但语义已乱,下游调用易出错
真正难的从来不是语法,而是确认“这个存储过程输出的结果,是否允许重复”。很多团队换 UNION ALL 只为图快,却没同步审查上游数据契约——这才是上线后慢得莫名其妙的根源。


















