union要求各select语句列数、数据类型及顺序严格一致,否则直接报错;union去重而union all保留重复且性能更高;order by和limit只能置于末尾作用于整体结果,列名以首个select为准。

UNION 不是万能拼接器,它只接受列数、类型、顺序都对齐的 SELECT 语句;错一个就报错,不是数据错乱,是直接执行失败。
UNION 要求每个 SELECT 的列数和类型严格一致
常见错误现象:ERROR 1222 (21000): The used SELECT statements have a different number of columns 或 Operand should contain 1 column(s)。这不是语法写错了,而是两个 SELECT 返回的列数不等,比如第一个写了 SELECT id, name,第二个却写了 SELECT id, name, created_at。
数据类型不兼容也会失败:比如一个 SELECT 中某列为 DATE 类型,另一个同位置列却是 VARCHAR,MySQL 或 PostgreSQL 会拒绝执行(SQL Server 可能隐式转换但不可靠)。
- 必须手动对齐字段:用
NULL、''、0占位补缺,例如SELECT id, name, NULL AS city FROM users和SELECT id, NULL AS name, city FROM locations - 别名只影响最终结果集列名,不影响校验 —— 列名是否一致不参与判断,只看实际返回的列结构
- 函数或表达式结果需显式转类型,如
CAST(created_at AS DATE)统一为日期类型
UNION 去重 vs UNION ALL 保留重复
默认 UNION 会做全字段去重,相当于加了 DISTINCT;而 UNION ALL 完全跳过去重逻辑,性能通常高 2–5 倍(尤其在百万级以上结果集)。
典型误用场景:把日志表按天分表后合并查询,用 UNION 导致慢得离谱,其实每天数据天然不重,该用 UNION ALL。
- 去重开销大:UNION 需临时排序 + 比较,内存/磁盘压力明显上升
- 业务上真需要去重?先确认:是同一主键跨表出现多次,还是只是字段值偶然相同?后者不该靠 UNION 解决
- 如果后续还要
GROUP BY或WHERE过滤,优先选UNION ALL,把去重逻辑交给上层处理更可控
ORDER BY 和 LIMIT 必须放在最后,且只生效一次
ORDER BY 不能写在每个 SELECT 后面,也不能放在中间;它只能出现在整个 UNION 语句末尾,作用于合并后的完整结果集。
同样,LIMIT 也只允许一个,写在最后才有效。想对每个子查询单独限制?不行 —— 得改用子查询包裹,例如:(SELECT * FROM t1 LIMIT 10) UNION ALL (SELECT * FROM t2 LIMIT 10)。
- 列名以第一个 SELECT 为准,所以
ORDER BY只能引用第一个 SELECT 中定义的列名或序号(如ORDER BY 2 DESC) - 如果第一个 SELECT 用了别名,后续排序必须用该别名,不能用原始字段名
- Access 等老系统不支持在 UNION 后直接
ORDER BY,需嵌套一层 SELECT 才行
跨库或跨排序规则时 COLLATE 显式统一很关键
当合并来自不同数据库(比如 utf8mb4_general_ci 和 utf8mb4_unicode_ci)或不同表(字段 COLLATE 不一致)的数据时,MySQL 可能报错:Illegal mix of collations。
这不是字符集问题,是排序规则冲突。解决方案不是改库,而是现场指定:
- 对字符串列加
COLLATE utf8mb4_unicode_ci,例如SELECT name COLLATE utf8mb4_unicode_ci FROM t1 UNION SELECT name COLLATE utf8mb4_unicode_ci FROM t2 - 避免在 WHERE 或 JOIN 条件中混用不同 COLLATE,UNION 只是暴露了底层不一致
- PostgreSQL 用户注意:它用
COLLATION关键字,语法略有不同,但原理相同
UNION 最容易被忽略的点:它不保证子查询执行顺序,也不提供中间结果命名能力;所有逻辑必须在单层 SQL 内完成,复杂过滤或计算得靠子查询或 CTE 铺垫 —— 直接硬塞 UNION 里只会让可读性和调试成本飙升。











