UNION比UNION ALL慢,因其强制走DISTINCT流程,需创建临时表并全量排序去重,时间复杂度升至O(n log n),而UNION ALL仅流式拼接、无去重开销。

UNION 默认去重导致临时表和排序开销
MySQL 执行 UNION 时,必须确保最终结果无重复行。它会把每个子查询结果先写入一个临时表(内存或磁盘),再对整个临时表做 DISTINCT 排序去重——这个过程在数据量稍大时就会明显变慢,尤其是当临时表溢出到磁盘时,I/O 成为瓶颈。
常见错误现象包括:EXPLAIN 显示 Using temporary; Using filesort、查询响应时间陡增、Created_tmp_disk_tables 状态值持续上升。
- 只要业务逻辑不依赖“自动去重”,就绝不该用
UNION -
UNION ALL完全跳过去重步骤,只是拼接结果集,性能几乎等同于执行两次独立SELECT - 即使两个子查询本身有重叠数据,也建议由应用层或后续
GROUP BY处理,而非交给UNION背锅
替换时必须检查字段类型与顺序一致性
UNION ALL 不校验语义,只做结构合并。如果子查询列数不同、对应列类型不兼容(比如 VARCHAR(50) 和 TEXT)、或隐式转换失败,MySQL 会直接报错:ERROR 1222 (21000): The used SELECT statements have a different number of columns 或类型冲突警告。
使用场景:跨归档表查用户信息、多租户分表聚合、日志按天分表汇总。
- 确保每个
SELECT的列数、顺序完全一致;别名统一用第一个子查询的列名 - 显式转换类型,例如:
CAST(created_at AS DATE)统一时间字段精度 - 避免
SELECT *,尤其当表结构未来可能不一致时——列数/类型错位会在上线后突然暴露
子查询仍需单独优化,UNION ALL 不是万能加速器
把 UNION 换成 UNION ALL 只消除了去重开销,但每个子查询本身若走全表扫描,整体还是慢。很多开发者误以为换完就万事大吉,结果发现 EXPLAIN 里每个 SELECT 都显示 type: ALL。
参数差异:WHERE 条件是否下推、索引是否覆盖、是否用了 LIMIT 控制中间结果大小,这些都直接影响最终耗时。
- 为每个子查询的
WHERE字段、ORDER BY字段建索引,优先考虑覆盖索引(包含所有SELECT列) - 把公共过滤条件写进每个子查询内部,而不是只放在外部包装层——MySQL 通常不会自动下推
- 如果最终只需前 100 行,优先在每个子查询里加
LIMIT 100,再UNION ALL,比最后统一LIMIT快得多
ORDER BY 和 LIMIT 放在 UNION ALL 外层要格外小心
当写成 (SELECT ... UNION ALL SELECT ...) ORDER BY id LIMIT 10 时,MySQL 必须先把全部子查询结果合并进临时表,再排序取前 10 行。若每个子查询返回 10 万行,临时表就是 20 万行,排序代价极高。
性能影响:内存不足时触发磁盘临时表,tmp_table_size 和 max_heap_table_size 配置不当会直接拖垮并发能力。
- 除非业务强依赖全局排序,否则改用“各子查询内
ORDER BY + LIMIT”再合并,然后应用层二次排序 - 若必须外层排序,确保
ORDER BY字段有索引,且临时表能留在内存中(调大上述两个配置) - 注意:外层
ORDER BY的列名必须来自第一个子查询的列名或别名,否则报错
UNION ALL 这个关键字本身,而是你忘了它只是“拼接”,不负责优化每个拼图块。每个 SELECT 都得自己扛得住压,否则再快的拼接也救不了慢查询。



















