union强制走临时表去重,union all仅流式拼接无去重;二者字段数、类型、顺序必须严格一致,order by/limit只能置于末尾,explain中union含union result和using temporary,union all则无。

UNION执行流程必须走临时表和去重
UNION不是简单拼接,它强制数据库走一套完整的去重流水线:先分别执行每个SELECT,把结果写入临时表;再对整个临时表做排序或哈希去重;最后返回唯一行。只要任一子查询带ORDER BY、LIMIT、聚合函数或用户变量,MySQL就一定触发临时表——哪怕你只查两行数据。
常见错误现象:Using temporary出现在EXPLAIN的Extra列里,说明已进入高开销路径;内存不足时还可能写磁盘临时文件,进一步拖慢查询。
- 去重判断基于整行完全一致(包括
NULL值),不是单字段比对 - 默认按第一列升序排序,想关掉得加
ORDER BY显式覆盖 - 字段数、类型、顺序必须严格一致,否则报错
ERROR 1222 (21000): The used SELECT statements have a different number of columns
UNION ALL只是流式拼接,不碰临时表
UNION ALL本质是“拉完就发”:执行第一个SELECT,边产出边返回;执行第二个SELECT,接着追加输出。全程不缓存中间结果,不排序,不比较,不建临时表。
性能差异直接体现在资源监控上:大表合并时,UNION ALL的CPU使用率通常不到UNION的1/3,内存峰值也低一个数量级。
- 如果源数据本身无重复(比如分表按时间分区),用
UNION纯属浪费资源 - 想保留重复但又需要最终排序,必须在末尾加
ORDER BY,不能在子句里加——否则语法报错 - 字段对齐要求和
UNION完全一样,类型不匹配照样报错,别以为“ALL”就能放松校验
EXPLAIN里一眼识别执行路径
看EXPLAIN输出是最直接的判断方式:UNION必然出现UNION RESULT节点,且Extra列带Using temporary;UNION ALL的Extra列为空或只有Using where这类常规提示。
示例:EXPLAIN SELECT id FROM t1 UNION SELECT id FROM t2会显示两行select_type为PRIMARY和UNION,最后一行select_type为UNION RESULT,table列为<union1></union1>——这个<union1></union1>就是临时表代号。
- 别信“小数据没区别”的经验,执行计划逻辑不变,只是延迟暴露
- 某些ORM生成的SQL自动套
UNION,要主动检查并替换为UNION ALL -
EXPLAIN FORMAT=JSON能看清是否启用filesort或temporary table,比文本格式更准
ORDER BY和LIMIT必须放在最后
无论用UNION还是UNION ALL,ORDER BY和LIMIT只能作用于整个合并结果,不能单独控制某个子查询——除非用括号包住子查询并配LIMIT,但这时MySQL会强制创建临时表,反而抵消UNION ALL优势。
错误写法:(SELECT a FROM t1 ORDER BY a LIMIT 10) UNION ALL (SELECT a FROM t2 ORDER BY a LIMIT 10)——看似取各表前10,实际先各自排序取10,再拼接,最后还得全局去重或排序,完全违背初衷。
- 真要限制总数,统一写在末尾:
... UNION ALL ... ORDER BY x LIMIT 20 - 想按不同规则排序子查询?不行,SQL标准不支持,得拆成应用层处理
- MySQL 8.0+支持CTE,但
WITH里的UNION ALL仍受同样约束,别指望绕过
UNION和UNION ALL里行为一致,但容易被忽略,导致去重结果不符合预期。











