MySQL中Union与UnionAll速度差异,主要在于去重操作对临时表产生的开销如何影响查询效率?

2026-04-30 21:171阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计693个文字,预计阅读时间需要3分钟。

MySQL中Union与UnionAll速度差异,主要在于去重操作对临时表产生的开销如何影响查询效率?

因为 `UNION` 不是合并重复再删除,而是强制走一遍 `DISTINCT` 流程——排序+临时表+行级比较。MySQL 必须先把所有子查询的结果写入一个临时表,再按所有列进行排序和行级比较,最后输出唯一的结果集。

所以,如果你只选择了 `user_id` 一列,并且语句中只有 `UNION`,它会对整行进行排序和比较。

EXPLAIN 能看到的两个关键标记:Using temporary 和 Using filesort

执行 EXPLAIN SELECT ... UNION SELECT ... 时,如果出现:
Using temporary:说明 MySQL 创建了内部临时表存中间结果
Using filesort:说明它正在对临时表做全量排序(不是用索引排序)
这两个标记同时出现,基本就等于“慢查询预警”。而 UNION ALLEXPLAIN 结果里,这两项通常都不会有。

去重逻辑不按单列,而是按整行匹配

常见误判:
- 认为 SELECT user_id FROM t1 UNION SELECT user_id FROM t2 是“按 user_id 去重”
- 实际上它等价于 SELECT DISTINCT user_id, NULL, NULL, ...(补齐所有列类型),只是你没显式写出其他列
- 如果两个子查询列数、类型、顺序不一致,MySQL 会报错,根本不会走到去重那步
- 更隐蔽的问题:NULL = NULLDISTINCT 中被判定为相等,但某些场景下你可能希望保留多条 NULL 值记录,这时 UNION 会悄悄吃掉它们

什么时候必须用 UNION,而不是硬套 UNION ALL

只有当业务逻辑明确要求“最终结果不能有重复整行”且无法靠上游保障时,才用 UNION
- 多张结构相同的过程表与归档表合并(如 orders_2025orders_his),且存在主键重叠风险
- 子查询来源不可控(比如来自不同 ETL 任务,未做去重清洗)
- 应用层无法容忍重复 ID+时间戳组合(例如用户行为流去重)
否则,优先用 UNION ALL;若真要单列去重,改写成 SELECT DISTINCT user_id FROM (SELECT ... UNION ALL SELECT ...) t,至少能控制去重粒度

临时表和排序不是“多花一点时间”,而是让查询从线性复杂度跳到 O(n log n),数据量过百万后,这个拐点来得很快。别等 EXPLAIN 报警才想起少写个 ALL

标签:Mysql

本文共计693个文字,预计阅读时间需要3分钟。

MySQL中Union与UnionAll速度差异,主要在于去重操作对临时表产生的开销如何影响查询效率?

因为 `UNION` 不是合并重复再删除,而是强制走一遍 `DISTINCT` 流程——排序+临时表+行级比较。MySQL 必须先把所有子查询的结果写入一个临时表,再按所有列进行排序和行级比较,最后输出唯一的结果集。

所以,如果你只选择了 `user_id` 一列,并且语句中只有 `UNION`,它会对整行进行排序和比较。

EXPLAIN 能看到的两个关键标记:Using temporary 和 Using filesort

执行 EXPLAIN SELECT ... UNION SELECT ... 时,如果出现:
Using temporary:说明 MySQL 创建了内部临时表存中间结果
Using filesort:说明它正在对临时表做全量排序(不是用索引排序)
这两个标记同时出现,基本就等于“慢查询预警”。而 UNION ALLEXPLAIN 结果里,这两项通常都不会有。

去重逻辑不按单列,而是按整行匹配

常见误判:
- 认为 SELECT user_id FROM t1 UNION SELECT user_id FROM t2 是“按 user_id 去重”
- 实际上它等价于 SELECT DISTINCT user_id, NULL, NULL, ...(补齐所有列类型),只是你没显式写出其他列
- 如果两个子查询列数、类型、顺序不一致,MySQL 会报错,根本不会走到去重那步
- 更隐蔽的问题:NULL = NULLDISTINCT 中被判定为相等,但某些场景下你可能希望保留多条 NULL 值记录,这时 UNION 会悄悄吃掉它们

什么时候必须用 UNION,而不是硬套 UNION ALL

只有当业务逻辑明确要求“最终结果不能有重复整行”且无法靠上游保障时,才用 UNION
- 多张结构相同的过程表与归档表合并(如 orders_2025orders_his),且存在主键重叠风险
- 子查询来源不可控(比如来自不同 ETL 任务,未做去重清洗)
- 应用层无法容忍重复 ID+时间戳组合(例如用户行为流去重)
否则,优先用 UNION ALL;若真要单列去重,改写成 SELECT DISTINCT user_id FROM (SELECT ... UNION ALL SELECT ...) t,至少能控制去重粒度

临时表和排序不是“多花一点时间”,而是让查询从线性复杂度跳到 O(n log n),数据量过百万后,这个拐点来得很快。别等 EXPLAIN 报警才想起少写个 ALL

标签:Mysql