union去重且隐式排序,union all不去重不排序、性能更高;90%场景优先选union all,除非业务强制要求唯一性和有序性。

UNION 和 UNION ALL 的核心区别在于去重逻辑:UNION 会自动去重并隐式排序,UNION ALL 直接合并结果集、不查重、不排序,因此性能更高。实际使用中,90% 以上的场景应优先考虑 UNION ALL,除非业务明确要求结果唯一且无序不可接受。
明确需求再选操作符
先问自己两个问题:结果是否允许重复行?是否关心返回顺序?
- 如果两张表主键/业务键完全不重叠(如按日期分表的销售数据),用 UNION ALL 安全又快
- 若需合并用户表和临时导入表,且存在相同手机号记录,用 UNION 可避免重复展示,但代价是额外排序和哈希去重开销
- UNION 的隐式排序不可靠——它只保证去重后结果有序,但排序依据是字段顺序而非显式 ORDER BY,不能替代真正的排序逻辑
列对齐与数据类型必须严格一致
UNION 系列操作要求各子查询的列数相同、对应位置列的数据类型兼容(如 int 与 bigint 可隐式转换,但 varchar(10) 与 text 在某些数据库中可能报错)。
- 建议显式写出列名,避免 SELECT *,防止因表结构变更导致列数或顺序错位
- 遇到类型不一致时,用 CAST 或 CONVERT 统一转换(例如把 DATE 转为 VARCHAR 做归一化展示)
- NULL 占位技巧很实用:某子查询缺第3列,可用 SELECT col1, col2, NULL AS col3 FROM t1 UNION ALL ... 补齐结构
性能优化关键点
真正影响效率的不是 UNION vs UNION ALL 的选择本身,而是背后的执行计划是否走索引、是否触发临时表或磁盘排序。
- 在 UNION 中,数据库通常会建临时哈希表或排序缓冲区去重,若结果集超内存阈值,就会写磁盘,性能断崖下跌
- 给参与 UNION 的每个子查询加合适的 WHERE 条件,比在外部套一层 WHERE 更有效(如 (SELECT ... WHERE status=1) UNION ALL (SELECT ... WHERE status=2))
- MySQL 8.0+ 和 PostgreSQL 支持对 UNION ALL 结果直接加索引提示(如 SQL_CALC_FOUND_ROWS 已弃用,改用窗口函数或单独 COUNT)
替代方案有时更合适
当 UNION 场景变复杂(如要带权重合并、做条件过滤后再去重、或需关联其他表),硬套 UNION 反而难维护。
- 多表 UNION ALL + 外层 GROUP BY 可模拟 UNION 效果,且能控制聚合逻辑(如取最新时间戳那条)
- 用 CTE 配合 ROW_NUMBER() 对全集编号,再按业务规则去重(例如 PARTITION BY user_id ORDER BY update_time DESC)
- 大批量静态数据合并,考虑提前物化视图或汇总表,避免每次查询都实时 UNION
不复杂但容易忽略:UNION 的去重是基于所有列的全字段比较,哪怕只是末尾一个空格或大小写差异,也会被视为不同行。需要语义去重时,记得提前用 TRIM、UPPER 或标准化函数清洗。










