UNION ALL仅支持合并SELECT语句,不能用于DML操作;它要求各子查询列数、类型严格一致,字段按位置对齐,最终列名取自首个SELECT,且整体排序和分页需置于末尾或子查询中。

UNION ALL 不能用于合并 DML 查询(如 INSERT、UPDATE、DELETE)的结果——它只接受 SELECT 语句。试图在 UNION ALL 中写 INSERT ... SELECT 或 UPDATE ... RETURNING 会直接报错,MySQL 不支持。
UNION ALL 只能接 SELECT,不是万能“拼接器”
很多人误以为 UNION ALL 像编程语言里的数组 concat(),能塞任何查询进去。实际它只是集合操作符,语法上强制要求左右两边都必须是完整、独立、可执行的 SELECT 语句。
- ✅ 正确:
SELECT id, name FROM t1 UNION ALL SELECT id, name FROM t2 - ❌ 错误:
INSERT INTO t1 SELECT * FROM t2 UNION ALL SELECT * FROM t3(语法错误) - ❌ 错误:
UPDATE t1 SET x=1 WHERE id=1 UNION ALL SELECT * FROM t2(解析失败)
MySQL 解析器在遇到 UNION ALL 时,会严格校验前一个和后一个 token 是否都是 SELECT 开头。一旦发现是 INSERT 或其他 DML 关键字,立刻终止并抛出类似 ERROR 1064 (42000): You have an error in your SQL syntax 的错误。
想“合并 DML 效果”?得换思路:用 SELECT 搭桥
真正的需求往往不是“拼 DML”,而是“把多个数据源的记录一起插入/更新”。这时应把 DML 拆成两步:先用 UNION ALL 拼好数据集,再用单条 DML 操作它。
- 要批量插入多张表的数据?写
INSERT INTO target (...) SELECT ... FROM (...) AS src,其中子查询用UNION ALL构建 - 要根据多来源计算更新值?把各路
SELECT合并后,用JOIN或UPDATE ... JOIN关联目标表 - 不能直接
UPDATE ... UNION ALL,但可以:UPDATE t1 JOIN (SELECT id, new_val FROM a UNION ALL SELECT id, new_val FROM b) AS u ON t1.id = u.id SET t1.val = u.new_val
字段对齐不严,SELECT 都会崩,更别说套 DML
即使全是 SELECT,UNION ALL 也极度敏感于列结构。这是最容易被忽略却导致静默错位的坑:
- 列数必须完全一致 —— 少一列,报错
ERROR 1222 (21000): The used SELECT statements have a different number of columns - 类型不兼容可能不报错但结果异常 —— 比如
TINYINT和DATETIME同位置合并,MySQL 会尝试转成字符串,0变成'0',2026-06-14变成'2026-06-14',数值比较全失效 - 别名只认第一个
SELECT—— 后面的AS xxx全被忽略,字段名错位后,你 INSERT 进去的可能是 name 写进 age 列
所以,如果要用 UNION ALL 为 DML 提供数据源,务必在子查询里显式对齐:SELECT id, name, NULL AS city, 't1' AS source FROM t1 UNION ALL SELECT id, NULL AS name, city, 't2' AS source FROM t2。
性能陷阱:UNION ALL 很快,但套在大子查询里可能拖垮整个 DML
UNION ALL 本身无去重开销,但若每个 SELECT 都扫大表、没索引、还带复杂 WHERE,那拼起来的中间结果集可能极大。而后续的 INSERT ... SELECT 或 UPDATE ... JOIN 会一次性加载或关联这个结果集。
- 查 2 张千万级日志表,各自
WHERE dt BETWEEN '2026-06-01' AND '2026-06-13',再UNION ALL—— 内存/临时表压力陡增 - 解决办法不是换
UNION,而是提前过滤、加索引、或分批次处理(比如按天循环执行) - 用
EXPLAIN看执行计划时,注意UNION ALL子查询是否触发了Using temporary; Using filesort
真正关键的不是“能不能用 UNION ALL”,而是你把它放在哪一层 —— 它只能做数据管道的“接头”,不能当扳手拧螺丝。


















