UNION去重并隐式排序,性能较低;UNION ALL不处理重复,性能高2–5倍;二者均要求列数、顺序及类型兼容,MySQL 8.0.31+才支持INTERSECT和EXCEPT,列名以首条SELECT为准,隐式类型转换易致精度丢失与索引失效。

MySQL 集合查询(UNION、UNION ALL、INTERSECT、EXCEPT)对新手不算友好——不是语法难,而是容易在语义理解、列对齐和隐式类型转换上栽跟头。
UNION 和 UNION ALL 的区别到底在哪?
新手常以为 UNION 就是“去重合并”,UNION ALL 就是“直接拼接”,但实际影响远不止结果行数:
-
UNION会触发隐式排序 + 去重,MySQL 内部可能临时创建唯一索引,数据量大时明显变慢;UNION ALL完全跳过这步,性能通常高 2–5 倍 - 两者都要求每个
SELECT的列数、顺序、类型兼容性一致;若第一列是INT,第二条的对应列是VARCHAR,MySQL 会尝试转成字符串,可能导致WHERE条件失效或索引无法使用 -
ORDER BY只能加在最后一条语句后,且只能按最终结果集的列名或位置编号(如ORDER BY 1),不能写在单个SELECT后面
SELECT id, name FROM users WHERE status = 1 UNION ALL SELECT user_id AS id, full_name AS name FROM admins WHERE active = 1 ORDER BY id;
MySQL 8.0+ 才支持 INTERSECT 和 EXCEPT
很多教程没说清楚版本限制:MySQL 在 8.0.31 之前根本不支持 INTERSECT 和 EXCEPT。新手照着文档写,直接报错 ERROR 1064 (42000)。
- 如果必须模拟交集,得用
INNER JOIN或子查询:SELECT * FROM a WHERE id IN (SELECT id FROM b) - 模拟差集(A - B)常用
LEFT JOIN ... IS NULL,比用NOT IN更安全(后者遇到NULL会整个结果为空) - 即使升级到 8.0.31+,
INTERSECT默认也去重,不带ALL关键字,这点和UNION行为不统一,容易混淆
列名和数据类型对齐是隐形雷区
集合查询的结果列名以第一条 SELECT 为准,后续语句的别名会被忽略;更麻烦的是 MySQL 对类型的“宽松”处理:
- 如果第一条选的是
DECIMAL(10,2),第二条是FLOAT,最终列类型可能是DOUBLE,精度丢失肉眼难察觉 -
TEXT和VARCHAR(255)混用时,UNION可能强制转成TEXT,导致GROUP BY或ORDER BY性能骤降 - 建议显式用
CAST()统一关键字段类型,比如:CAST(created_at AS DATETIME)
真正卡住新手的,往往不是会不会写 UNION,而是查出来结果“看起来对”,但分页错乱、统计不准、接口响应变慢——问题藏在类型隐式转换和执行计划里,得学会看 EXPLAIN FORMAT=TREE 输出里的 union_result 节点。










