默认应优先使用 union all,因其不排序、不建临时表、不去重,性能比 union 高2–5倍;但要求各子查询列数、顺序、类型严格对齐,否则报错或数据错位,且 order by/limit 仅允许出现在末尾。

默认就该用 UNION ALL,除非你明确需要去重;它不排序、不建临时表、不比对重复行,性能通常比 UNION 高 2–5 倍。
列数、顺序、类型必须严格对齐,否则报错或结果错位
MySQL 不按字段名匹配,只看位置。第一个 SELECT 的列结构就是最终结果集的模板,后续所有 SELECT 必须完全对齐:
- 列数不同 → 直接报错:
ERROR 1222 (21000): The used SELECT statements have a different number of columns - 顺序错位 → 数据写进错误列:比如
SELECT name, id FROM t1和SELECT id, name FROM t2合并后,name值会进id列 - 类型不兼容 → 可能隐式转成宽泛类型(如
VARCHAR(255)),或报错:Operand should contain 1 column(s);危险组合包括DATE与CHAR、BIT与TEXT - 缺字段?用
NULL或CAST占位:SELECT id, name, NULL AS city FROM t1,别依赖自动补空
ORDER BY 和 LIMIT 只能加在最后,中间加会报错
UNION ALL 是集合操作,不是嵌套结构。每个子查询单独加 ORDER BY 或 LIMIT 会触发语法错误:This type of clause is not allowed in a UNION。
- 想整体排序?必须写在末尾:
(SELECT id FROM t1) UNION ALL (SELECT id FROM t2) ORDER BY id DESC - 想取 Top N?先子查询内限流,再合并,最后外层再限流:
SELECT * FROM ((SELECT id FROM order_2025_q4 ORDER BY created_at DESC LIMIT 200) UNION ALL (SELECT id FROM order_2026_q1 ORDER BY created_at DESC LIMIT 200)) AS t ORDER BY created_at DESC LIMIT 100 - 如果某边数据量极大但业务只要最新 5 条,不提前
LIMIT就是浪费 IO —— 外层排序前已把百万行全读入内存或磁盘临时表
性能瓶颈往往不在 UNION ALL 本身,而在子查询和排序
UNION ALL 在 MySQL 5.7+ 默认不强制建临时表,但 EXPLAIN 中仍可能看到 Using temporary 或 Using filesort,问题通常出在:
- 外层
ORDER BY:即使只写一次,MySQL 也得把全部结果暂存后排序 —— 这是排序行为本身导致的,不是UNION ALL的锅 - 子查询里有
GROUP BY、DISTINCT或窗口函数:它们各自物化,和UNION ALL无关 - 类型不一致引发隐式转换:比如
VARCHAR(10)和VARCHAR(255)合并,MySQL 按 255 分配内存,后续若用于GROUP BY或JOIN,可能因长度超限导致索引失效 - 没下推
WHERE:不要指望优化器自动把过滤条件推到各子查询,显式写进去更可靠 ——SELECT id FROM t1 WHERE status = 'active'UNION ALLSELECT id FROM t2 WHERE status = 'active'
最容易被忽略的是字段语义错位:两个表都有 status 字段,一个表示订单状态,一个表示用户激活状态,UNION ALL 后它们强行挤进同一列,业务逻辑可能已经崩了,但 SQL 看不出任何异常。











