UNION ALL 性能远超 UNION,因前者纯流式拼接、不建临时表、不排序、不去重;后者强制创建临时表、全字段排序去重,内存不足时触发磁盘 I/O,100 万行下耗时高达 UNION ALL 的 11 倍以上。

UNION ALL 比 UNION 快得多,不是“稍微快”,而是量级差异
除非你明确需要去重,否则用 UNION 就是在给 MySQL 加临时表、排序、磁盘 I/O 这三重负担。实测中,100 万行数据时 UNION 耗时是 UNION ALL 的 11 倍以上——这不是配置问题,是底层机制决定的。
UNION 实际执行的是 DISTINCT + ORDER BY,不是“简单去重”
MySQL 处理 UNION 时,并不会聪明地边读边判重。它必须:
- 先把所有子查询结果全写进临时表(
Using temporary) - 对整张临时表按所有列排序(
Using filesort),只为去重 - 内存不够时直接刷到磁盘,I/O 成瓶颈
- 即使你显式写了
ORDER BY,数据库仍可能排序两次:一次为去重,一次为你写的
而 UNION ALL 就是纯拼接:子查询 A 输出一行,立刻发给客户端;A 完了接 B,B 也流式输出,中间零阻塞。
列数、类型、顺序必须严格一致,但别名和表达式可以不同
两个 SELECT 的列数要一样,对应列类型要兼容(比如 INT 和 TINYINT 可以,VARCHAR 和 TEXT 在某些版本会隐式转换失败),顺序不能错。但以下写法完全合法:
SELECT id, name FROM users_2023 UNION ALL SELECT user_id AS id, CONCAT(first_name, ' ', last_name) AS name FROM users_2024;
注意:AS 别名只影响最终列名,不参与类型/顺序校验;真正校验的是 SELECT 后面的实际表达式结构。
ORDER BY 只能放在最后,子查询里写无效
这是最常被忽略的陷阱。下面这个写法是错的:
SELECT id FROM t1 ORDER BY create_time DESC UNION ALL SELECT id FROM t2 ORDER BY create_time DESC;
MySQL 会直接忽略两个子查询里的 ORDER BY。正确写法只有一种:
SELECT id FROM t1 UNION ALL SELECT id FROM t2 ORDER BY id;
如果你真要按时间字段排,且子查询结果本身无序,就得靠外层 ORDER BY —— 但要注意:加了它,UNION ALL 也会触发排序,性能优势就打折扣了。所以优先在子查询里用索引保证局部有序,再靠外层最小化排序范围。
真正难的不是语法,而是判断“要不要去重”。很多团队用 UNION 只是因为“怕重复”,但实际数据天然不重(比如按日期分表的日志、按 ID 段拆分的用户表)。这时候硬套 UNION,等于主动给自己埋性能雷。



















