MySQL 用 UNION 和 UNION ALL 的区别

FreeGuideOnline 最新 2026-07-05

MySQL UNION 与 UNION ALL 的区别

UNION 和 UNION ALL 都是 MySQL 中用于合并两个或多个 SELECT 语句结果集的操作符。它们的核心区别在于:UNION 会自动去除重复行,而 UNION ALL 会保留所有行(包括重复行)。 这个差异在查询效率、数据完整性和使用场景上会产生显著影响。

一、基础语法与快速对比

两个操作符的语法几乎相同:

SELECT column1, column2 FROM table1
UNION [ALL]
SELECT column1, column2 FROM table2;
  • 列数必须相同:所有 SELECT 语句选取的列数量必须一致。
  • 数据类型兼容:对应列的数据类型应当兼容或可以隐式转换。
  • 列名:最终结果集使用第一个 SELECT 语句的列名。

下面的表格直观展示了两者的区别:

特性 UNION UNION ALL
重复行处理 自动过滤,结果集中每行唯一 保留所有重复行
性能 较慢,需要额外的排序和去重操作 很快,直接合并结果集
排序 默认会进行排序(用于去重) 不保证顺序(除非显式 ORDER BY)
适用场景 需要纯净、无重复数据的合并结果 允许或期望保留所有记录,强调性能

二、通过示例理解去重行为

假设有两张表 employees_cnemployees_us,记录员工姓名。

employees_cn

name
张三
李四
王五

employees_us

name
王五
Alice
Bob

使用 UNION ALL

SELECT name FROM employees_cn
UNION ALL
SELECT name FROM employees_us;

结果(保留 6 行,重复的“王五”出现两次):

张三
李四
王五
王五
Alice
Bob

使用 UNION

SELECT name FROM employees_cn
UNION
SELECT name FROM employees_us;

结果(仅保留 5 行,“王五”被去重):

张三
李四
王五
Alice
Bob

三、性能差异与执行原理

UNION 之所以比 UNION ALL 慢,是因为它在幕后做了更多工作:

  1. UNION ALL 执行流程

    • 依次执行每个 SELECT 语句。
    • 将返回的所有行直接拼接到结果集中。
    • 无任何额外操作,因此速度很快。
  2. UNION 执行流程

    • 分别执行所有 SELECT 语句。
    • 将全部行放入临时表(或直接进行排序比较)。
    • 对合并后的数据集执行 DISTINCT 操作(通常通过排序或哈希方式),过滤掉完全相同的数据行。
    • 这个过程涉及额外的 CPU、内存以及可能产生临时磁盘 I/O,在数据量较大时开销显著。

重要提示:UNION 的去重是基于 整行 的判断。所有选中的列组合在一起完全相同时,才会被视为重复行。

四、排序规则的区别

  • UNION:由于要去重,MySQL 通常会对结果进行排序(在比较过程中)。如果没有外层 ORDER BY,最终返回的顺序可能看起来是按某列“自然”排序的,但这不是保证的行为,依赖它是不安全的。
  • UNION ALL:仅做简单拼接,行顺序与各 SELECT 语句的执行顺序和数据库实际检索顺序有关,同样不具确定性。如果需要排序,必须显式使用 ORDER BY

对整个合并结果排序(语句末尾加 ORDER BY):

SELECT name, '中国' AS region FROM employees_cn
UNION ALL
SELECT name, '美国' FROM employees_us
ORDER BY name;

注意:ORDER BY 只能出现在最后一条 SELECT 语句之后,且会作用于整个合并结果。

五、如何选择:决策指南

情况 推荐操作
你肯定各 SELECT 结果集本身就没有重复,或你完全不关心重复 务必使用 UNION ALL,性能最优
你需要合并的结果中每一行都必须唯一 使用 UNION
源表数据量巨大,但需要去重 尽量用 UNION;若性能无法接受,可尝试在应用层去重,或利用临时表优化
需要保留重复记录用于后续统计(如实数计数) 必须使用 UNION ALL
想通过 UNION 对两个子查询去重,但部分列需要忽略 可使用 UNION ALL 结合外层 GROUP BY 灵活控制

开发中非常常见的错误:默认使用 UNION 而不是 UNION ALL。除非业务明确要求去重,否则一律优先使用 UNION ALL 以获得最佳性能。

六、高级技巧与注意事项

1. 在子查询中使用 UNION ALL

UNION ALL 可嵌套,常用作派生表:

SELECT name, COUNT(*) AS cnt
FROM (
    SELECT name FROM employees_cn
    UNION ALL
    SELECT name FROM employees_us
) AS combined
GROUP BY name
HAVING cnt > 1;

此查询可以找出在两个表中都出现过至少一次的名字。如果内层误用 UNION,那么 cnt 将永远是 1,导致逻辑错误。

2. UNION 与索引

UNION/UNION ALL 对各个 SELECT 语句的索引利用情况取决于各自的 WHERE 条件。合并之后的结果集没有索引。因此,若需对最终结果过滤或联接,考虑先物化为临时表或优化各子查询。

3. 数据类型严格性

当对应列的数据类型不完全相同时,MySQL 会进行隐式转换。例如:

SELECT id FROM table1
UNION
SELECT name FROM table2;  -- 如果 id 是整数,name 是字符串,结果将统一为字符串

为避免意外,尽量让对应列的类型明确一致,或在 SELECT 时显式转换。

七、总结

  • UNION ALL:合并所有行,性能高,保留重复。是处理大数据量合并的首选。
  • UNION:合并并去重,性能低,结果集唯一。仅在确实需要去重时使用。
  • 永远先考虑用 UNION ALL,当且仅当业务要求数据唯一时才改用 UNION
  • 注意列数量、类型匹配,以及通过显式 ORDER BY 控制最终输出顺序。

掌握了这些区别,你就能在 SQL 查询中做出正确的选择,同时兼顾结果的正确性与系统的执行效率。