集合运算
集合运算
🌍 生活类比
想象你有两堆名片:
- UNION — 把两堆名片合在一起,重复的只留一张
- UNION ALL — 把两堆名片全部合在一起,重复的也保留
- INTERSECT — 只挑出两堆中都有的人
- EXCEPT — 从第一堆中去掉第二堆也有的人
集合运算就是对两个查询结果集进行"合并、取交、取差"的操作。
🎯 核心概念
UNION — 去重合并
将两个结果集合并,自动去除重复行。
SELECT city FROM employees
UNION
SELECT city FROM departments;
UNION ALL — 不去重合并
将两个结果集合并,保留所有重复行。性能更好,因为不需要去重。
SELECT city FROM employees
UNION ALL
SELECT city FROM departments;
INTERSECT — 交集
返回两个结果集中都存在的行。
SELECT city FROM employees
INTERSECT
SELECT city FROM departments;
EXCEPT / MINUS — 差集
返回在第一个结果集中存在、但第二个结果集中不存在的行。
-- EXCEPT(SQL Server、PostgreSQL)
SELECT city FROM employees
EXCEPT
SELECT city FROM departments;
-- MINUS(Oracle)
SELECT city FROM employees
MINUS
SELECT city FROM departments;
列匹配规则
使用集合运算时,两个 SELECT 必须满足:
| 规则 | 说明 |
|---|---|
| 列数相同 | 两个 SELECT 的列数必须一致 |
| 类型兼容 | 对应列的数据类型必须兼容 |
| ORDER BY 放最后 | 只能在整个语句末尾使用一次 |
-- 正确:两列,类型匹配
SELECT first_name, salary FROM employees
UNION
SELECT department_name, budget FROM departments;
-- 错误:列数不同
SELECT first_name, salary FROM employees
UNION
SELECT department_name; -- ❌ 列数不匹配
💡 何时使用哪种运算
| 运算 | 场景 | 是否去重 | 性能 |
|---|---|---|---|
| UNION | 需要合并且去重 | ✅ 是 | 较慢 |
| UNION ALL | 合并不需要去重 | ❌ 否 | 较快 |
| INTERSECT | 找共同部分 | ✅ 是 | 中等 |
| EXCEPT | 找差异部分 | ✅ 是 | 中等 |
UNION ALL,性能更好。
📝 基本语法
-- UNION 语法
SELECT column1, column2 FROM table1
UNION
SELECT column1, column2 FROM table2
[ORDER BY column1];
-- UNION ALL 语法
SELECT column1, column2 FROM table1
UNION ALL
SELECT column1, column2 FROM table2;
-- INTERSECT 语法
SELECT column1, column2 FROM table1
INTERSECT
SELECT column1, column2 FROM table2;
-- EXCEPT 语法
SELECT column1, column2 FROM table1
EXCEPT
SELECT column1, column2 FROM table2;
- 集合运算中,列名以第一个 SELECT 的列名为准
- ORDER BY 只能出现在最后,且通常按列序号排序
- 每个 SELECT 内可以有自己的 WHERE、GROUP BY
📌 示例
示例:查询所有人员的姓名来源
合并员工姓名和部门名称到一个结果集中。
-- 查看所有"人名"来源:员工 + 部门经理
SELECT first_name AS name, '员工' AS 来源
FROM employees
UNION ALL
SELECT department_name, '部门'
FROM departments
ORDER BY 来源;
说明:添加一个常量列标记数据来源,便于区分。
示例:找出有员工但没有部门的城市
-- 有员工的城市 减去 有部门的城市
SELECT city FROM employees
EXCEPT
SELECT city FROM departments;
思路:用 EXCEPT 做差集,快速找出"只在 employees 中出现的城市"。
🎬 场景演练
场景一:合并多渠道客户名单
公司有线上和线下两个客户表,需要合并去重后生成完整客户名录。
-- 线上客户
SELECT customer_name, email, '线上' AS channel
FROM online_customers
UNION
-- 线下客户
SELECT customer_name, email, '线下'
FROM offline_customers
ORDER BY customer_name;
要点:用 UNION 而非 UNION ALL,自动去除重复客户。
场景二:对比两个月的销售差异
找出本月有销售但上月没有销售的产品。
-- 本月销售的产品
SELECT product_id FROM orders
WHERE order_date >= '2026-06-01'
EXCEPT
-- 上月销售的产品
SELECT product_id FROM orders
WHERE order_date >= '2026-05-01'
AND order_date < '2026-06-01';
要点:EXCEPT 天然适合"找差异"的场景。
❓ 常见问题
Q:UNION 和 UNION ALL 什么时候该用哪个? A: 如果不需要去重,优先用
UNION ALL,因为它不需要额外的去重步骤,性能更好。只有确定需要去重时才用UNION。
Q:集合运算中列名不一致会报错吗? A: 不会,结果集的列名以第一个
SELECT的列名为准。但列数和对应列的数据类型必须一致。
Q:INTERSECT 和 JOIN 有什么区别? A:
INTERSECT是按整行去匹配交集,而JOIN是按指定条件关联。如果需要"找完全相同的行"用INTERSECT,需要"按某个字段关联不同表的列"用JOIN。
Q:MySQL 支持 INTERSECT 和 EXCEPT 吗? A: MySQL 8.0 起支持
INTERSECT和EXCEPT。如果使用更早版本,需要用INNER JOIN和NOT EXISTS/LEFT JOIN ... IS NULL来模拟。
📖 小节
| 运算 | 作用 | 是否去重 |
|---|---|---|
| UNION | 合并两个结果集 | ✅ |
| UNION ALL | 合并两个结果集(保留重复) | ❌ |
| INTERSECT | 取两个结果集的交集 | ✅ |
| EXCEPT | 取两个结果集的差集 | ✅ |
- 列数和类型必须匹配
ORDER BY只能放在语句最后- 确定无重复时优先使用
UNION ALL提升性能
📝 作业
- 使用
UNION合并employees表中工资高于 8000 的员工和departments表中预算高于 100000 的部门名称。 - 使用
INTERSECT找出employees和departments表中都存在的城市。 - 使用
EXCEPT找出有部门但没有员工被分配的城市。 - 思考:如果两个查询结果的列类型不完全一致(如 INT 和 VARCHAR),集合运算会发生什么?
下一课
下一节我们将学习 约束与键 — 了解如何用 PRIMARY KEY、FOREIGN KEY 等约束保证数据完整性。



