UNION 和 UNION ALL 的核心区别在于是否去重:UNION 自动去重(隐式 DISTINCT),UNION ALL 直接拼接;前者性能低需排序去重,后者无额外开销;两者均要求列数相同、类型兼容,列名以首个 SELECT 为准。

UNION 和 UNION ALL 的核心区别在哪
根本区别在于是否去重:UNION 会自动去除重复行,UNION ALL 完全不处理重复,直接拼接结果集。这意味着 UNION 内部隐式执行了 DISTINCT 操作,而 UNION ALL 只是物理追加。
实际影响很直接:
-
UNION性能更低,尤其在大数据量时,需排序 + 去重 -
UNION ALL几乎无额外开销,执行更快 - 两者都要求各
SELECT子句列数相同、对应列类型兼容(MySQL 会尝试隐式转换) - 列名以第一个
SELECT的字段名为准
什么时候必须用 UNION,不能用 UNION ALL
当你明确需要合并后结果唯一时,比如统计多个渠道的用户 ID 且不能重复计数,或拼接不同条件查出的主键集合用于后续 IN 查询 —— 这时若用 UNION ALL,可能引入重复 ID 导致逻辑错误或性能下降(如子查询中重复值被多次匹配)。
典型场景包括:
- 多表联合去重取 ID 列表
- 分页前对多个来源数据统一去重再排序
- 和
NOT IN/LEFT JOIN ... IS NULL配合做排除逻辑,重复值会影响结果正确性
ORDER BY 和 LIMIT 在 UNION 中怎么写才合法
ORDER BY 和 LIMIT 不能出现在单个子查询里(除最外层),否则报错:ERROR 1221 (HY000): Incorrect usage of UNION and ORDER BY。
正确写法是把整个 UNION 当作一个派生表,或直接在最后加 ORDER BY 和 LIMIT:
SELECT id, name FROM t1 UNION ALL SELECT id, name FROM t2 ORDER BY id DESC LIMIT 10;
注意:
-
ORDER BY中的字段名必须来自第一个SELECT的列(或其别名) - 如果想对每个子查询单独排序再合并,得用子查询包装:
(SELECT ... ORDER BY x LIMIT n) UNION ALL (SELECT ... ORDER BY y LIMIT m) -
LIMIT放在末尾是对合并后整体生效;若要限制每个子查询,必须加括号并分别写
常见错误:列类型不一致导致 UNION 失败
MySQL 对 UNION 各子查询对应列的类型有隐式转换规则,但不是万能的。比如 VARCHAR(10) 和 TEXT 合并可能触发警告或截断;INT 和 DECIMAL(10,2) 通常可转,但 JSON 和 TEXT 混用会报错:ERROR 1267 (HY000): Illegal mix of collations 或类型不兼容。
稳妥做法:
- 显式用
CAST()或CONVERT()统一类型,例如:CAST(created_at AS DATE) - 避免混用
NULL和非空字符串,可用COALESCE(col, '')对齐 - 字符集/排序规则不一致时,用
COLLATE utf8mb4_0900_as_cs显式指定
真正容易被忽略的是:即使语句能跑通,隐式转换也可能悄悄改变数据表现——比如时间字段被转成字符串后排序失效,或者数字精度丢失。上线前务必用真实数据验证结果一致性。










