UNION要求列数相同且对应列类型兼容,去重开销大,推荐优先用UNION ALL;ORDER BY须置于末尾并引用首查询字段;子查询中LIMIT需套派生表;类型隐式转换易致去重异常,应显式统一类型。
MySQL 的 UNION 不是简单拼接两组结果,它强制要求左右两个 SELECT 语句返回的列数完全相同,且对应位置的列在隐式转换下能兼容。比如 INT 和 VARCHAR 可能被转成字符串合并,但 JSON 和 BLOB 在某些版本会报错。
SELECT 为准,后续的列名会被忽略ORDER BY,除非配合 LIMIT(否则语法报错)ORDER BY 必须写在最后一个子句之后,且引用的是第一个 SELECT 的字段别名或位置序号(如 ORDER BY 1)默认的 UNION 会自动去重,MySQL 内部要对合并后的临时结果做排序 + 去重操作,数据量大时明显拖慢;而 UNION ALL 直接追加,零额外开销。如果你能确认两边结果天然无交集(比如查不同状态的订单、不同日期的分区表),就该用 UNION ALL。
UNION 等价于 UNION DISTINCT,显式写出更易读SELECT DISTINCT,不能靠 UNION 实现这是新手高频报错点:(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)
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;
当两个 SELECT 中同一列一边是 INT、另一边是 VARCHAR,MySQL 会尝试转成一个公共类型(通常是字符串),这时数值 0 和字符串 '0' 会被认为相等,UNION 去重时可能意外合并;同理,NULL 和空字符串在某些字符集下也可能被当作相同值处理。
CAST() 或 CONVERT() 统一类型最稳妥UNION ALL 先看原始数据,再切回 UNION 观察去重效果
字段时,注意 DATETIME 和 TIMESTAMP 在时区处理上的差异可能导致表面相同实则不等实际用的时候,先跑通 UNION ALL,再决定是否需要去重;列对齐和类型一致性,比写法“漂亮”重要得多。