union比union all慢,因其强制执行排序+临时表+整行比较的distinct流程,时间复杂度升至o(n log n),而union all仅流式拼接、无去重开销。

UNION 为什么比 UNION ALL 慢:去重不是简单删几行
因为 UNION 不是“合并完再删重复”,而是强制走一遍 DISTINCT 流程——这背后是排序 + 临时表 + 行级比较。MySQL 必须把所有子查询结果先写入一个临时结构(内存或磁盘),再按所有列联合排序,最后逐行扫描去重。哪怕你只选了 user_id 一列,只要语句写的是 UNION,它仍会对整行做排序比较。
EXPLAIN 能看到的两个关键标记:Using temporary 和 Using filesort
执行 EXPLAIN SELECT ... UNION SELECT ... 时,如果出现:Using temporary:说明 MySQL 创建了内部临时表存中间结果Using filesort:说明它正在对临时表做全量排序(不是用索引排序)
这两个标记同时出现,基本就等于“慢查询预警”。而 UNION ALL 的 EXPLAIN 结果里,这两项通常都不会有。
去重逻辑不按单列,而是按整行匹配
常见误判:
- 认为 SELECT user_id FROM t1 UNION SELECT user_id FROM t2 是“按 user_id 去重”
- 实际上它等价于 SELECT DISTINCT user_id, NULL, NULL, ...(补齐所有列类型),只是你没显式写出其他列
- 如果两个子查询列数、类型、顺序不一致,MySQL 会报错,根本不会走到去重那步
- 更隐蔽的问题:NULL = NULL 在 DISTINCT 中被判定为相等,但某些场景下你可能希望保留多条 NULL 值记录,这时 UNION 会悄悄吃掉它们
什么时候必须用 UNION,而不是硬套 UNION ALL
只有当业务逻辑明确要求“最终结果不能有重复整行”且无法靠上游保障时,才用 UNION:
- 多张结构相同的过程表与归档表合并(如 orders_2025 和 orders_his),且存在主键重叠风险
- 子查询来源不可控(比如来自不同 ETL 任务,未做去重清洗)
- 应用层无法容忍重复 ID+时间戳组合(例如用户行为流去重)
否则,优先用 UNION ALL;若真要单列去重,改写成 SELECT DISTINCT user_id FROM (SELECT ... UNION ALL SELECT ...) t,至少能控制去重粒度
临时表和排序不是“多花一点时间”,而是让查询从线性复杂度跳到 O(n log n),数据量过百万后,这个拐点来得很快。别等 EXPLAIN 报警才想起少写个 ALL。











