一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

为什么MySQL在执行UNION操作时建议使用UNION ALL

时间:2026-07-04 10:37:46 编辑:袖梨 来源:一聚教程网

UNION ALL 性能远高于 UNION,因其不执行去重和隐式排序,避免临时表与文件排序;UNION 等价于 UNION ALL 后加 DISTINCT,触发 Using temporary 和 Using filesort,导致 I/O 与 CPU 开销剧增,且结果行数和顺序不可控。

UNION ALL 比 UNION 快得多,因为不触发去重逻辑

MySQL 执行 UNION 时,会隐式做两件事:先合并所有子查询结果,再对整个结果集去重(等价于加了 DISTINCT),这通常意味着排序或哈希扫描;而 UNION ALL 就是把各分支结果直接拼起来,不碰数据内容。实测中,两个各返回 50 万行的子查询,UNION ALL 可能在 200ms 内流式返回,UNION 却卡在 Using temporary; Using filesort 上,耗时数秒甚至 OOM。

常见错误现象包括:

  • 执行计划里出现 Using temporaryUsing filesort
  • 查询响应时间随结果集增长非线性飙升
  • 临时表空间(tmp_table_size / max_heap_table_size)被频繁打满

UNION 会悄悄改掉你的结果行数和顺序

只要任意两行在所有列上完全相等,UNION 就会干掉一个——哪怕它们来自不同业务含义的表(比如「正式员工」和「外包人员」都叫张三、部门相同),也会被当成重复行过滤掉。这不是 bug,是设计行为。

更隐蔽的是顺序问题:UNION 去重过程通常伴随隐式排序(尤其 MySQL 8.0 以前),导致结果顺序不可控;而 UNION ALL 至少能保持各子查询的原始输出顺序(除非你显式加 ORDER BY)。

使用场景举例:

  • 合并按天分表的日志(log_20260501, log_20260502…)→ 不需要去重,用 UNION ALL
  • 跨库汇总用户注册数 → 各库 ID 独立,不可能重复,用 UNION ALL
  • 补全缺失维度值(如左连接后用 UNION ALL 加默认分类)→ 明确要保留所有行

列对齐、类型兼容、ORDER BY 写法必须严格按规则来

UNIONUNION ALL 都不是“智能拼接”,它们只认位置,不认字段名。下面这些写法都会报错:

  • SELECT name, id FROM t1 UNION SELECT id, name FROM t2 → 列顺序错,第一列是 t1.namet2.id 拼的,语义混乱
  • SELECT created_at FROM orders UNION SELECT order_time FROM history → 类型不兼容(比如 DATETIME vs TIMESTAMP 在某些版本会失败)
  • SELECT x FROM a ORDER BY x LIMIT 10 UNION SELECT y FROM b ORDER BY y LIMIT 10 → 语法非法,MySQL 直接抛 ERROR 1221

正确做法:

  • 统一用 CAST()CONVERT(... USING utf8mb4) 显式转类型
  • 所有子查询列数、顺序、别名必须一致,例如都写成 SELECT id AS uid, name AS fullname FROM ...
  • ORDER BY 只能放在最后,且只能引用列名或位置序号:... UNION ALL ... ORDER BY fullname
  • 想各自取 top N?包一层子查询:(SELECT ... ORDER BY x LIMIT 10) UNION ALL (SELECT ... ORDER BY y LIMIT 10)

什么时候真该用 UNION?别靠感觉判断

只有当以下**全部满足**时,才考虑 UNION

  • 业务逻辑明确要求结果中每行全局唯一(比如合并多个来源的用户 ID,且 ID 是主键)
  • 你无法在子查询里通过 WHEREJOIN 提前排重
  • 数据量不大,或已确认去重开销可控(比如总行数

如果只是“怕有重复所以保险起见”,那反而容易埋坑——比如某天上游数据逻辑变更,导致本不该去重的行被意外合并,问题很难回溯。更稳妥的做法是:先用 UNION ALL 查出全量,再用 SELECT DISTINCT 包一层(虽然性能略差,但语义清晰、可调试)。

最容易被忽略的一点:即使两个表结构一模一样,UNION 也会把 NULL 和 NULL 当作相等去重——而很多业务场景里,NULL 的语义是“未知”,不是“相同”。这点在统计类查询中极易引发偏差。

热门栏目