union要求列数、类型、顺序严格一致,否则直接报错;列数不齐用null等占位,类型不兼容需显式转换;union all性能更高且无需去重;order by/limit仅作用于最终结果且只能出现一次。

UNION 不是“拼起来就行”,列数、类型、顺序三者错一个,直接报错,不是结果错乱,是根本执行不了。
UNION 报 ERROR 1222:列数不一致怎么办
这是最常见也最容易被忽略的硬性失败点。比如写:SELECT id, name FROM users 和 SELECT id, name, created_at FROM logs,MySQL 会立刻抛出 ERROR 1222 (21000): The used SELECT statements have a different number of columns。
- 必须手动对齐列数:缺列就用
NULL、''或0占位,例如SELECT id, name, NULL AS city FROM users对应SELECT id, NULL AS name, city FROM locations - 别名不影响校验 ——
AS nickname只改最终列名,不参与结构比对 - 子查询里也不能绕过:哪怕
(SELECT id, name FROM t1)套一层,里面还是得和另一侧列数一致
数据类型不兼容导致 UNION 失败
列数对了,但同位置列类型不匹配,同样执行失败。比如左侧是 DATE,右侧同位置是 VARCHAR,PostgreSQL 和 MySQL 都拒绝隐式转换(SQL Server 可能侥幸通过,但不可靠)。
- 显式转类型是唯一稳妥方式:用
CAST(created_at AS DATE)或CONVERT(DATE, created_at) - 数值和字符串不能混:
SELECT 42和SELECT '42'在多数引擎中不兼容,需统一为CAST(42 AS CHAR)或CAST('42' AS SIGNED) - 注意 NULL 的类型推断:
NULL默认类型模糊,建议写成CAST(NULL AS VARCHAR(50))明确意图
UNION vs UNION ALL:什么时候该用哪个
默认 UNION 等价于 UNION DISTINCT,会触发全字段排序+去重,开销显著;UNION ALL 跳过这步,性能通常高 2–5 倍。
- 天然无重复场景必须用
UNION ALL:如按天分表的日志合并(log_20260801、log_20260802),主键不可能跨天重复 - 真需要去重?先确认是同一逻辑主键重复,还是字段值偶然相同 —— 后者不该靠
UNION解决,应在上层用GROUP BY或应用层处理 - 如果后续还要
WHERE过滤或GROUP BY,优先选UNION ALL,把去重逻辑留给更可控的环节
ORDER BY 和 LIMIT 只能放在最后,且只生效一次
ORDER BY 不能写在每个 SELECT 后,也不能插在中间;LIMIT 同理。它们只作用于最终合并结果,且只能出现一次。
- 想对每个子集单独限制?必须包成子查询:
(SELECT * FROM t1 ORDER BY ts DESC LIMIT 5) UNION ALL (SELECT * FROM t2 ORDER BY ts DESC LIMIT 5) - 排序字段只能引用第一个
SELECT中定义的列名或序号,例如ORDER BY 2 DESC或ORDER BY user_name(不能用name,除非第一个 SELECT 里写了AS user_name) - Access 等老系统不支持直接
UNION ... ORDER BY,得套一层SELECT * FROM (...) AS tmp ORDER BY ...
最难缠的不是语法写错,而是列结构表面一致、实际类型隐式冲突,或者误以为 UNION 能替代业务去重逻辑 —— 它只认字节级相等,NULL 和空字符串、不同精度的数字都可能被当成不同值,也可能被当成相同值,取决于引擎实现。











