该用 UNION ALL:若允许重复或确定无重复;必须唯一才用 UNION,因 UNION 会去重排序导致性能下降,而 UNION ALL 仅拼接结果集。

UNION 和 UNION ALL 哪个该用?看要不要去重
直接说结论:如果结果里允许重复、或者你明确知道不会重复,就用 UNION ALL;如果必须保证每行唯一,才用 UNION。别凭感觉选,否则查 100 万行时慢得明显。
原因很简单:UNION 内部会建临时表 + 去重(类似用 DISTINCT),还会隐式触发排序(MySQL 8.0+ 优化后不一定排,但去重逻辑仍在);UNION ALL 就是把结果集“拼起来”,不碰数据内容。
- 常见错误现象:
SELECT * FROM a UNION SELECT * FROM b比UNION ALL慢几倍,且执行计划里出现Using temporary; Using filesort - 使用场景举例:合并日志表(每天一张)、分库查询汇总、补全缺失维度值——这些通常不需要去重,
UNION ALL更合适 - 注意:哪怕两个表结构完全一样,只要某一行字段值全等,
UNION就会干掉一个;UNION ALL不管它是不是“看着一样”
列数、类型、顺序必须严格一致,否则直接报错
UNION 和 UNION ALL 都不是“智能拼接”,它们要求每个 SELECT 的输出列数量、类型兼容性、甚至别名位置都对齐。错一个,MySQL 就抛 ERROR 1222 (21000): The used SELECT statements have a different number of columns 或类型转换失败。
- 列数不匹配:立刻报错,没商量
- 类型不兼容:比如
VARCHAR(10)和TEXT在某些 MySQL 版本会隐式转,但JSON和INT就直接失败;建议显式CAST()或统一用CONVERT(... USING utf8mb4) - 顺序陷阱:
SELECT name, id FROM t1 UNION SELECT id, name FROM t2不会自动按字段名对齐,而是按位置——结果第一列是t1.name和t2.id拼的,大概率乱套
ORDER BY 只能写在最后,不能每个 SELECT 后加
很多人想给每个子查询单独排序再合并,比如 SELECT x FROM a ORDER BY x LIMIT 10 UNION SELECT y FROM b ORDER BY y LIMIT 10——这语法非法,MySQL 会报 ERROR 1221 (HY000): Incorrect usage of UNION and ORDER BY。
正确做法只在末尾加一次 ORDER BY,且只能引用列名或位置序号:
SELECT id, name FROM users UNION ALL SELECT id, dept_name AS name FROM departments ORDER BY name;
- 如果要用别名排序,确保所有
SELECT中该列有相同别名(如上例的name) - 想先各自取 top N 再合并?得包一层子查询:
(SELECT ... ORDER BY x LIMIT 10) UNION ALL (SELECT ... ORDER BY y LIMIT 10) -
UNION ALL结果顺序 = 第一个SELECT输出顺序 + 第二个SELECT输出顺序……不保证全局有序,所以ORDER BY几乎总是需要的
NULL 值和空字符串在去重时怎么算?
UNION 去重时,NULL = NULL 是成立的——也就是说,两行其他字段都一样,仅一列是 NULL,会被视为重复行;而空字符串 '' 和 NULL 永远不等,不会被误去重。
- 典型坑:从不同来源查用户邮箱,有的填了
NULL,有的填了'',用UNION合并后,NULL行只剩一个,但''行全保留 - 如果业务上认为
NULL和''等价,得提前统一:COALESCE(email, '')或IFNULL(email, '') -
UNION ALL完全不管这个,NULL行照单全收,适合做原始数据归档类场景
实际用的时候,最常被忽略的是:你以为没重复,其实有;你以为要唯一,其实业务上根本不在乎。先跑个 SELECT COUNT() FROM (SELECT ... UNION ALL ...) t 和 SELECT COUNT() FROM (SELECT ... UNION ...) t 对比一下差多少,比拍脑袋选快得多。











