UNION ALL 与 UNION 的核心区别在于前者不排序、不去重、性能更高,而后者会隐式去重排序;使用时须确保列数一致、类型兼容、ORDER BY 和 LIMIT 仅能置于末尾,且需警惕大结果集引发的临时表与内存问题。

UNION ALL 和 UNION 的区别到底在哪
用 UNION ALL 是为了不丢数据、不排序、不去重,性能比 UNION 高一截。如果你两个查询结果里天然没重复(比如查不同日期的订单、不同状态的用户),硬用 UNION 反而多一次隐式去重扫描,白耗 CPU 和临时空间。
常见错误现象:UNION 后字段顺序对不上,MySQL 直接报错 ERROR 1222 (21000): The used SELECT statements have a different number of columns;或者字段类型隐式转换失败,比如一边是 INT,一边是 VARCHAR,MySQL 尝试转但精度丢失或截断。
- 列数必须完全一致 —— 不是“看起来差不多”,是 SELECT 后面显式写出的字段个数
- 对应位置的字段类型最好一致;若不一致,MySQL 会按「更宽泛类型」隐式转换(如
INT和DECIMAL会转成DECIMAL),但VARCHAR(10)和VARCHAR(100)没问题,TINYINT和DATETIME就大概率出错 - 列名以第一个
SELECT的为准,后面语句的 AS 别名不影响最终列名
字段对齐时最容易漏掉的三件事
很多人写完两个 SELECT 就直接加 UNION ALL,运行报错才回头找——其实问题基本就出在字段对齐上。
- SELECT 列表里不能有“多一个少一个”的字段:比如第一个查
id, name, created_at,第二个就不能只写id, name或多加个source,得补NULL AS created_at或'static' AS source - 函数返回值类型要小心:
NOW()是DATETIME,CURDATE()是DATE,混在一起可能触发隐式转换警告甚至截断 - 聚合查询和非聚合查询不能直接拼:
SELECT user_id FROM orders和SELECT COUNT(*) FROM orders GROUP BY user_id列数、语义都不匹配,得先统一成同结构(比如都带user_id,COUNT 结果作为一列)
ORDER BY 和 LIMIT 只能放在最后
UNION ALL 是集合操作,不是子查询嵌套,所以中间每个 SELECT 不能单独加 ORDER BY 或 LIMIT(MySQL 8.0.22+ 允许单个子句加,但意义不大,且容易误导)。
常见错误现象:写成 (SELECT ... ORDER BY id LIMIT 10) UNION ALL (SELECT ... ORDER BY id LIMIT 10),MySQL 报错或忽略前面的 ORDER BY,最终结果乱序、数量也不对。
- 真要限制总数,只能在整个
UNION ALL外再套一层SELECT * FROM (...) AS t LIMIT 20 - 真要全局排序,
ORDER BY必须写在最后,且只能引用最终列名或位置(如ORDER BY 1表示第一列) - 如果某一边需要“取最新一条”,得提前用窗口函数或子查询处理好,再参与
UNION ALL
大结果集合并时的内存与临时表风险
UNION ALL 本身不排序不去重,看似轻量,但如果两边结果都很大(比如各 50 万行),MySQL 还是得把全部结果暂存在内存或磁盘临时表里,才能返回给客户端。
典型表现:查询变慢、Created_tmp_tables 和 Created_tmp_disk_tables 状态变量飙升、甚至触发 max_heap_table_size 或 tmp_table_size 限制导致报错 ERROR 1105 (HY000): Out of memory。
- 确认是否真需要一次性拉全量:前端分页?那就该在应用层分别查、再合并逻辑,而不是全扔给 MySQL
- 检查执行计划:
EXPLAIN看type是否为ALL,Extra有没有Using temporary—— 如果有,说明已落地磁盘临时表 - 避免在
UNION ALL中使用未索引字段排序或 GROUP BY,这会让优化器更倾向走临时表
字段类型不一致、列数不对、ORDER BY 放错位置、临时表撑爆内存——这四点占了实际开发中 90% 的 UNION ALL 问题。调的时候别急着改语法,先 EXPLAIN 看执行计划,再逐条核对字段定义。










