SQL中UNION与UNION ALL的区别(详解去重机制、性能差异与选型场景)

SQL 中 UNION 与 UNION ALL 都用于合并多个 SELECT 的结果集,核心区别在于是否去重:UNION 自动剔除重复行(等价于先 UNION ALL 再做 DISTINCT),UNION ALL 原样保留所有行。因为去重需要额外的排序或哈希开销,UNION 通常比 UNION ALL 慢得多——据 sqlqueries.in 的实测,合并两年产品名时 UNION 约 5.2 秒、UNION ALL 仅 2.1 秒。经验法则是:除非明确需要去重,否则默认用 UNION ALL。据 dbsyntax 的方言对照,二者均为 ANSI 标准,在 PostgreSQL、MySQL、SQL Server、Oracle、SQLite 中行为一致。

一、核心区别:一行之差在去重

UNION 像一次隐式的 DISTINCT,把合并后的结果集按整行去重;UNION ALL 只是把各段结果首尾拼接。用 SQLProStudio 的经典例子说明:

sql

-- 表 A: 1, 2, 3   表 B: 2, 3, 4
SELECT id FROM table_a UNION SELECT id FROM table_b;
-- 结果: 1, 2, 3, 4 (重复行被去除)

SELECT id FROM table_a UNION ALL SELECT id FROM table_b;
-- 结果: 1, 2, 3, 2, 3, 4 (所有行保留)

腾讯 ima 知识库同样将其概括为”UNION 自动去除重复记录,UNION ALL 保留所有记录不做去重处理”。这个差异直接决定了二者在性能与语义上的取舍。

二、性能差距来自去重代价

UNION 必须为整份合并结果做排序或哈希以识别重复,数据量越大代价越高;UNION ALL 仅做追加,不做任何额外处理。下表汇总关键差异(综合 sqlqueries.in、OneUptime、dbsyntax):

对比维度 UNION UNION ALL
重复行 去除 保留
性能 较慢(需排序/哈希去重) 较快(直接拼接)
是否隐式排序 是(去重过程可能重排) 否(多数引擎保左后右顺序)
结果正确性 去重后更安全 行可能被意外折叠
适用默认 否 是(分析场景首选)

OneUptime 在 MySQL 视角下指出,UNION 的去重使引擎必然创建临时表,而 UNION ALL 在某些情形下可避免临时表。当结果集达十亿行量级时,dbsyntax 形容二者差距是”分钟级对比秒级”。

三、合并的硬性规则

无论用哪种,两段 SELECT 必须满足:

  • 列数相同:每段返回的列数量一致,否则直接报错。
  • 类型兼容:对应列的数据类型需可隐式转换(如 INT 与 BIGINT 可合并,多数引擎会向更宽类型转换)。
  • 列名取首段:结果集的列名来自第一个 SELECT,后续段的列名被忽略。
sql

-- 错误:列数不一致
SELECT id, name FROM customers UNION ALL SELECT id FROM suppliers;
-- 正确:列数、顺序对应
SELECT id, name FROM customers UNION ALL SELECT order_id, customer_name FROM orders;

四、什么时候用 UNION

只有当重复行会导致结果错误时,才值得付出去重代价。典型场景:

求唯一值集合。例如跨”客户表”和”员工表”取所有不重复的邮箱:

sql

SELECT email FROM customers UNION SELECT email FROM staff;

跨多份目录取唯一分类。把 2024 与 2025 两个商品目录的分类合并去重,得到完整分类清单。sqlqueries.in 把”生成唯一客户列表”列为 UNION 的标准用例。

五、什么时候用 UNION ALL

绝大多数合并其实不需要去重,此时 UNION ALL 既快又准确:

合并互不重叠的分区。按年/季度分表存储时,各分区数据天然不重叠:

sql

SELECT revenue FROM sales_q1
UNION ALL SELECT revenue FROM sales_q2
UNION ALL SELECT revenue FROM sales_q3
UNION ALL SELECT revenue FROM sales_q4;

OneUptime 强调,若此处误用 UNION,当不同季度出现相同金额时会被错误折叠,导致汇总总额偏低。

日志与全量分析。日志表天然允许重复,分析时只需全量行,UNION ALL 避免无谓去重。

聚合前合并。先把多源数据 UNION ALL 成一个集合,再统一 GROUP BY,写法清晰且高效。

六、ORDER BY 与多路合并写法

ORDER BY 只能放在整段 UNION 的最后,作用于合并后的总结果;不能在单个 SELECT 内直接排序(除非包成子查询)。列名需引用第一段:

sql

SELECT id, name, 'customer' AS type FROM customers
UNION ALL
SELECT id, name, 'supplier' AS type FROM suppliers
ORDER BY name;

多路合并可链式叠加任意数量的分支;若要对每段先取 Top N 再合并,需把每段包进子查询并各自加 ORDER BY ... LIMIT。

七、方言差异需注意

二者均为 ANSI 标准,在 PostgreSQL、MySQL、SQL Server、Oracle、SQLite、Snowflake、DuckDB、Redshift 上行为一致。唯一显著的例外是 BigQuery:它要求显式写出 UNION ALL 或 UNION DISTINCT,裸写 UNION 会报语法错误(dbsyntax 与 OneUptime 均提及)。此外 INTERSECT(两集交集)与 EXCEPT/MINUS(差集)与它们同属集合运算家族,Oracle 用 MINUS 表达差集,MySQL 8.0.31+ 才支持 INTERSECT/EXCEPT。

常见问题(FAQ)

UNION ALL 一定比 UNION 快吗?
在任何非极小的数据集上都更快,因为它跳过去重所需的排序或哈希;UNION 等价于 UNION ALL 后加 DISTINCT。

两段查询列数不同能合并吗?
不能,必须列数相同且对应列类型兼容,否则引擎直接报错。

结果列名以哪个为准?
以第一个 SELECT 的列名为准,后续段的列名被忽略,但列数和顺序必须对齐。


本文更新于 2026 年 8 月,内容综合SQLProStudio、sqlqueries.in、OneUptime、dbsyntax 等公开技术资料整理。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 qiqicto@qq.com 举报,一经查实,本站将立刻删除。
赞 (0)
赵其鑫的头像赵其鑫管理团队

相关推荐

返回顶部