union会自动去重并隐式排序,union all仅拼接结果集不处理重复;二者均要求列数相同、类型兼容,列名以首个select为准,order by/limit须置于整个语句末尾。

UNION 和 UNION ALL 的核心区别在哪
关键就一条:UNION 会自动去重并隐式排序,UNION ALL 不做任何处理,只拼接结果集。这意味着如果两个查询有重复行,UNION 返回 1 行,UNION ALL 返回 2 行;而且 UNION 的执行开销明显更高——它得建临时表、排序、比对。
实际开发中,90% 以上的合并场景应该优先选 UNION ALL,除非你明确需要去重且不介意性能损耗。
用 UNION / UNION ALL 前必须满足的条件
两个(或多个)SELECT 语句能合并在一块,不是靠“名字像”就行,SQL 强制要求:
- 列数量必须一致
- 对应位置的列数据类型要兼容(比如
INT和BIGINT可以,DATE和VARCHAR通常不行) - 列名以第一个
SELECT为准,后续查询的列名会被忽略
常见错误是字段数不一致,报错信息通常是:SELECTs to the left and right of UNION do not have the same number of columns。遇到这个,别急着改语法,先用 DESCRIBE 或 SELECT * FROM ... LIMIT 1 对比两边字段结构。
ORDER BY 和 LIMIT 怎么加才有效
ORDER BY 和 LIMIT 不能写在每个子查询后面,只能放在整个 UNION 语句末尾,且只作用于最终合并结果。
错误写法:SELECT id FROM t1 ORDER BY id LIMIT 5 UNION SELECT id FROM t2 ORDER BY id LIMIT 5 → 语法报错
正确写法:(SELECT id FROM t1) UNION ALL (SELECT id FROM t2) ORDER BY id LIMIT 10
注意括号不是必须的,但加上更清晰;另外,如果想对某一部分单独排序再合并,得用子查询包装,例如:SELECT * FROM (SELECT id FROM t1 ORDER BY create_time DESC LIMIT 3) AS a UNION ALL SELECT * FROM t2
NULL 值和类型隐式转换带来的坑
当两列类型不完全一致时(比如一边是 VARCHAR(10),另一边是 VARCHAR(50)),MySQL 会按更宽的类型对齐;PostgreSQL 则更严格,可能直接报错 UNION types text and integer cannot be matched。
NULL 值在 UNION 中会被当成具体值参与去重——也就是说,SELECT NULL UNION SELECT NULL 只返回一行 NULL;而 UNION ALL 会返回两行。
容易被忽略的是字符串比较:某些数据库(如 SQL Server)默认大小写不敏感,'abc' 和 'ABC' 在 UNION 中会被当作同一行;换到 MySQL 的 utf8mb4_bin 排序规则下,它们就是两行。










