UNION要求列数相同且对应列类型兼容,去重开销大,推荐优先用UNION ALL;ORDER BY须置于末尾并引用首查询字段;子查询中LIMIT需套派生表;类型隐式转换易致去重异常,应显式统一类型。

UNION 要求列数和对应类型必须兼容
MySQL 的 UNION 不是简单拼接两组结果,它强制要求左右两个 SELECT 语句返回的列数完全相同,且对应位置的列在隐式转换下能兼容。比如 INT 和 VARCHAR 可能被转成字符串合并,但 JSON 和 BLOB 在某些版本会报错。
- 列名以第一个
SELECT为准,后续的列名会被忽略 - 不能对单个子查询加
ORDER BY,除非配合LIMIT(否则语法报错) - 想按整结果排序,
ORDER BY必须写在最后一个子句之后,且引用的是第一个SELECT的字段别名或位置序号(如ORDER BY 1)
UNION vs UNION ALL:去重开销很实在
默认的 UNION 会自动去重,MySQL 内部要对合并后的临时结果做排序 + 去重操作,数据量大时明显拖慢;而 UNION ALL 直接追加,零额外开销。如果你能确认两边结果天然无交集(比如查不同状态的订单、不同日期的分区表),就该用 UNION ALL。
-
UNION等价于UNION DISTINCT,显式写出更易读 - 去重逻辑基于所有列的全值比较,不是主键或某几列
- 如果只想要某几列去重,得在外层套
SELECT DISTINCT,不能靠UNION实现
子查询里不能直接用 LIMIT(除非配 ORDER BY)
这是新手高频报错点:(SELECT * FROM t1 LIMIT 1) UNION (SELECT * FROM t2) 会提示 This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' —— 实际上不是 LIMIT 本身的问题,而是 MySQL 限制了带 LIMIT 的子查询出现在 UNION 左右两侧(5.7+ 仍存在此限制)。
- 绕过方法:把带
LIMIT的查询包一层派生表,例如(SELECT * FROM (SELECT * FROM t1 ORDER BY id LIMIT 1) AS tmp) - 注意:派生表必须有别名,否则语法错误
- 如果只是想取合并后的前 N 条,直接在
UNION整体后加LIMIT即可
SELECT id, name FROM users WHERE status = 1 UNION ALL SELECT id, name FROM users WHERE status = 2 ORDER BY id DESC LIMIT 10;
NULL 和隐式类型转换容易引发意料外结果
当两个 SELECT 中同一列一边是 INT、另一边是 VARCHAR,MySQL 会尝试转成一个公共类型(通常是字符串),这时数值 0 和字符串 '0' 会被认为相等,UNION 去重时可能意外合并;同理,NULL 和空字符串在某些字符集下也可能被当作相同值处理。
- 显式用
CAST()或CONVERT()统一类型最稳妥 - 测试时用
UNION ALL先看原始数据,再切回UNION观察去重效果 - 涉及时间字段时,注意
DATETIME和TIMESTAMP在时区处理上的差异可能导致表面相同实则不等
实际用的时候,先跑通 UNION ALL,再决定是否需要去重;列对齐和类型一致性,比写法“漂亮”重要得多。









