UNION
导学
有时你需要把两个结构相同但来源不同的查询结果合并在一起展示。UNION 就是做这个的——它像胶水一样把多个 SELECT 的结果纵向拼接。
定义
UNION:用于合并两个或多个 SELECT 语句的结果集。被合并的查询必须拥有相同数量的列,且对应列的数据类型兼容。
核心语法
SELECT 列1, 列2 FROM 表A
UNION [ALL]
SELECT 列1, 列2 FROM 表B
[ORDER BY 列1];
UNION:合并后去重,性能开销较大UNION ALL:合并后不去重,性能更好
完整示例准备:建表与数据
CREATE TABLE employees (
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(20),
dept VARCHAR(20),
score DECIMAL(5,2)
);
INSERT INTO employees (emp_name, dept, score) VALUES
('大翔', '技术部', 100),
('白歌', '技术部', NULL);
完整示例一:UNION ALL 合并不同条件的结果
当前数据状态
employees 表:
| emp_id | emp_name | dept | score |
|---|---|---|---|
| 1 | 大翔 | 技术部 | 100 |
| 2 | 白歌 | 技术部 | NULL |
执行 UNION ALL
SELECT emp_name, score, '有分数' AS 类别 FROM employees WHERE score IS NOT NULL
UNION ALL
SELECT emp_name, score, '无分数' AS 类别 FROM employees WHERE score IS NULL;
操作后的结果
| emp_name | score | 类别 |
|---|---|---|
| 大翔 | 100.00 | 有分数 |
| 白歌 | NULL | 无分数 |
结果解读
UNION ALL直接将两个 SELECT 的结果纵向拼接- 共 1 + 1 = 2 行,保留所有行
- 使用常量列
'有分数'和'无分数'标识数据来源
完整示例二:UNION 与 UNION ALL 的区别对比
当前数据状态
基于上面的 employees 表。
执行 UNION(去重)
SELECT emp_name, dept FROM employees
UNION
SELECT emp_name, dept FROM employees;
操作后的结果
| emp_name | dept |
|---|---|
| 大翔 | 技术部 |
| 白歌 | 技术部 |
执行 UNION ALL(不去重)
SELECT emp_name, dept FROM employees
UNION ALL
SELECT emp_name, dept FROM employees;
操作后的结果
| emp_name | dept |
|---|---|
| 大翔 | 技术部 |
| 白歌 | 技术部 |
| 大翔 | 技术部 |
| 白歌 | 技术部 |
结果解读对比
| 特性 | UNION | UNION ALL |
|---|---|---|
| 结果行数 | 2 行(去重后) | 4 行(保留重复) |
| 性能 | 需要创建临时表并去重,性能开销大 | 直接追加结果,性能更好 |
| 适用场景 | 需要确保结果唯一时 | 确定无重复或允许重复时 |
UNION需要去重,MySQL 会创建临时表并做唯一性检查- 如果业务上确定没有重复(或允许重复),务必使用
UNION ALL
完整示例三:UNION 后的排序
当前数据状态
基于上面的 employees 表。
执行 UNION ALL 并排序
SELECT emp_name, score, '正式员工' AS 类型 FROM employees WHERE score >= 90
UNION ALL
SELECT emp_name, score, '待考核' AS 类型 FROM employees WHERE score IS NULL
ORDER BY score DESC;
操作后的结果
| emp_name | score | 类型 |
|---|---|---|
| 大翔 | 100.00 | 正式员工 |
| 白歌 | NULL | 待考核 |
结果解读
ORDER BY必须放在最后,对整个合并后的结果集排序- 不能对每个
SELECT单独加ORDER BY(除非用括号 +LIMIT,不推荐) - 先按 score 降序排列,NULL 排在最后
完整示例四:列数不同导致的错误
当前数据状态
基于上面的 employees 表。
错误写法演示
-- 错误:列数不匹配
SELECT emp_name FROM employees
UNION
SELECT emp_name, dept FROM employees;
报错信息
ERROR 1222 (21000): The used SELECT statements have a different number of columns
正确写法
-- 正确:列数必须相同
SELECT emp_name, dept FROM employees
UNION
SELECT emp_name, dept FROM employees;
结果解读
- 被合并的查询必须拥有相同数量的列
- 对应列的数据类型必须兼容
- 结果列名取第一个
SELECT的列名(或别名)
完整示例五:列类型不兼容的对比
当前数据状态
基于上面的 employees 表。
类型兼容的写法
SELECT emp_id, emp_name FROM employees
UNION ALL
SELECT emp_id, emp_name FROM employees;
操作后的结果
| emp_id | emp_name |
|---|---|
| 1 | 大翔 |
| 2 | 白歌 |
| 1 | 大翔 |
| 2 | 白歌 |
结果解读
emp_id和emp_name在两个查询中类型一致,可以正常合并- 如果类型不兼容(如一个 INT 一个 VARCHAR),MySQL 会尝试隐式类型转换,可能产生警告或错误
完整示例六:UNION 与 ORDER BY 和 LIMIT 的配合
当前数据状态
基于上面的 employees 表。
执行查询
SELECT emp_name, score, '高分' AS 类别 FROM employees WHERE score >= 90
UNION ALL
SELECT emp_name, score, '其他' AS 类别 FROM employees WHERE score IS NULL
ORDER BY score DESC
LIMIT 2;
操作后的结果
| emp_name | score | 类别 |
|---|---|---|
| 大翔 | 100.00 | 高分 |
| 白歌 | NULL | 其他 |
结果解读
ORDER BY放在最后,对合并后的整个结果集排序LIMIT限制最终返回的行数- 取所有记录中按 score 排序的前 2 条
完整示例七:UNION 与 JOIN 的区别对比
当前数据状态
基于上面的 employees 表。
UNION 纵向合并
SELECT emp_name, score FROM employees WHERE score >= 90
UNION ALL
SELECT emp_name, score FROM employees WHERE score IS NULL;
结果:2 行,每行有 emp_name 和 score 两列。
JOIN 横向拼接
SELECT a.emp_name, a.score AS score_a, b.score AS score_b
FROM employees a
LEFT JOIN employees b ON a.emp_id = b.emp_id AND b.score IS NOT NULL;
操作后的结果
| emp_name | score_a | score_b |
|---|---|---|
| 大翔 | 100.00 | 100.00 |
| 白歌 | NULL | NULL |
结果解读对比
| 特性 | UNION | JOIN |
|---|---|---|
| 方向 | 纵向合并(增加行) | 横向拼接(增加列) |
| 列数要求 | 两个查询列数必须相同 | 不要求列数相同 |
| 连接条件 | 不需要连接条件 | 需要 ON 条件 |
| 适用场景 | 合并同类数据 | 关联不同表的数据 |
常见误区
| 误区 | 正解 |
|---|---|
UNION 和 UNION ALL 一样 | UNION 去重慢,UNION ALL 不去重快。确定无重复时用 ALL。 |
| 每个 SELECT 都可以加 ORDER BY | 只有最后一个 SELECT 后面可以加 ORDER BY,对整个结果集生效。 |
| 列名不一致没关系 | 列数必须相同,类型必须兼容。结果列名取第一个 SELECT 的列名。 |
面试考点
Q:UNION 和 JOIN 的区别?
UNION是纵向合并(增加行),要求列数相同;JOIN是横向拼接(增加列),要求有连接条件。
Q:为什么 UNION ALL 比 UNION 快?
UNION需要创建临时表并对合并结果去重(类似DISTINCT),涉及哈希或排序操作;UNION ALL直接追加结果,无需去重。
Q:UNION 结果集的列名由谁决定?
由第一个
SELECT语句的列名(或别名)决定。
小结
UNION纵向合并多个 SELECT 的结果UNION ALL不去重,性能优于UNION- 被合并的查询必须列数相同、类型兼容
- 只有最后一个 SELECT 后面可以加全局 ORDER BY
下一章引子:合并了结果集,接下来学习如何对数据进行分组汇总——GROUP BY。