union all 是合并多个查询结果最快的方式,因跳过去重与排序;需列数、顺序、类型严格对齐,order by/limit 仅限末尾,字段别名以首句为准,隐式转换有风险应显式 cast。

UNION ALL 是合并多个查询结果最快的方式,只要你不依赖去重,就该默认用它。
为什么 UNION ALL 比 UNION 快得多
UNION 会隐式执行 DISTINCT + 隐式排序,MySQL 必须把所有结果写入临时表、排序、逐行比对去重——数据量一过几万行,就容易触发磁盘临时表甚至内存溢出。UNION ALL 完全跳过这一步,只是把各 SELECT 的结果集顺序追加,零额外开销。
- UNION 实际等价于
UNION DISTINCT,且 MySQL 8.0+ 对 NULL 和浮点精度的去重更敏感,行为反而更难预测 - 即使两个子查询天然无重复(比如按日期分区的日志表),用 UNION 也白白多一次扫描
- 如果真需要去重,应该在业务层或外层
SELECT DISTINCT显式控制,而不是靠 UNION 隐式承担
列数、顺序、类型必须严格对齐
报错 ERROR 1222 (21S01): The used SELECT statements have a different number of columns 是最常见的拦路虎。MySQL 不看字段名,只校验「位置」和「表达式类型兼容性」。
- 每个
SELECT的列数必须完全一致;不能靠SELECT *蒙混,表结构一变就崩 - 顺序必须语义对齐:比如第一个查的是
id, name, status,后面就不能写成name, id, status - 类型要兼容:
INT和DECIMAL可转,VARCHAR(10)和VARCHAR(100)也能拼,但TINYINT和DATETIME强行 UNION ALL 很可能报错或返回0/'0000-00-00' - 缺字段就用
NULL或CAST()占位,例如:SELECT id, name, NULL AS city FROM t1和SELECT id, NULL AS name, city FROM t2
ORDER BY 和 LIMIT 只能放在最后
UNION ALL 是集合操作,不是嵌套结构。你不能在每个子句后加 ORDER BY 或 LIMIT,MySQL 会直接报语法错误。
- 要整体排序:写在末尾,如
UNION ALL ORDER BY created_at DESC - 要限制总条数:用
UNION ALL LIMIT 100 - 若某一边需取最新 5 条(比如日志表按时间倒序取头),必须先包成子查询:
(SELECT * FROM log_202605 ORDER BY created_at DESC LIMIT 5),再参与 UNION ALL - 字段别名以第一个
SELECT为准,后续子句里的AS xxx全部被忽略
实际合并场景中的典型写法
比如合并线上用户和线下用户两表,主键不重叠、字段略有差异:
SELECT id, name, phone, 'online' AS source FROM users_online UNION ALL SELECT id, real_name AS name, NULL AS phone, 'offline' AS source FROM users_offline;
注意这里用了 real_name AS name 确保第二列语义对齐,NULL AS phone 填补缺失字段,'offline' 字面量统一来源标识。最终结果列名是 id、name、phone、source,全部来自第一句。
最容易被忽略的是类型隐式转换风险——看着都是数字,但一边是 INT UNSIGNED,另一边是 SIGNED,在大结果集下可能触发截断或警告,建议关键字段显式 CAST(... AS SIGNED) 统一。











