本节摘要:集合操作把多个查询结果按集合规则合并。UNION 并集、INTERSECT 交集、EXCEPT 差集。本节讲它们的用法和 UNION 与 UNION ALL 的区别。
阅读完本节,你应当能够:
集合操作把两个查询结果按集合规则合并,要求两查询的列数和类型对应:
-- 北京和上海的客户合一起 SELECT * FROM Customers WHERE City = '北京' UNION SELECT * FROM Customers WHERE City = '上海';
| 操作 | 去重 | 性能 |
|---|---|---|
| UNION | 去重 | 慢(要排序去重) |
| UNION ALL | 不去重 | 快 |
-- UNION ALL:保留重复,快 SELECT Amount FROM Orders WHERE CustomerID = 101 UNION ALL SELECT Amount FROM Orders WHERE CustomerID = 102;
💡 关键直觉:确定没重复或要保留重复时用 UNION ALL,它省去重开销快很多。只有需要去重才用 UNION。
-- 既有订单又活跃的客户(交集) SELECT CustomerID FROM Orders INTERSECT SELECT CustomerID FROM ActiveCustomers; -- 有订单但没激活的客户(差集) SELECT CustomerID FROM Orders EXCEPT SELECT CustomerID FROM ActiveCustomers;
注意 EXCEPT 在 Oracle 叫 MINUS,且不是所有 DBMS 都支持 INTERSECT/EXCEPT(MySQL 老版本不支持,要用 JOIN/IN 模拟)。
集合操作要求两查询:
-- 列数和类型要对应 SELECT FirstName, City FROM Customers UNION SELECT ProductName, Category FROM Products; -- 两列,类型对应即可
不支持 INTERSECT/EXCEPT 的 DBMS,可用 JOIN 或 IN 模拟:
-- INTERSECT 用 IN 模拟 SELECT DISTINCT CustomerID FROM Orders WHERE CustomerID IN (SELECT CustomerID FROM ActiveCustomers); -- EXCEPT 用 NOT IN 模拟 SELECT DISTINCT CustomerID FROM Orders WHERE CustomerID NOT IN (SELECT CustomerID FROM ActiveCustomers);
⚠️ 常见坑:用 UNION 合并时忘了列数要相同,报错。集合操作两查询的列数和类型必须对应,结果列名取第一个。
第 4 章结束。聚合、JOIN、子查询、集合操作四件套在手,绝大多数分析查询都能写了。下一章进入数据操作——增删改。
Q1:UNION 和 UNION ALL 性能差多少?
UNION 要去重(内部做排序或哈希),数据量大时明显慢;UNION ALL 直接拼接,几乎无额外开销。如果两个查询的结果本就不会重复(如按互斥条件分别查),直接用 UNION ALL 更划算。这是常见的性能优化点。
Q2:两个查询的列数、列类型必须一致吗?
UNION 系列要求:列数一致、对应列类型兼容(能隐式转换)。列名不要求一致,结果集的列名取第一个查询的。不满足会直接报错。写之前先对齐列结构,这是最常见的报错原因。
Q3:INTERSECT 和 EXCEPT 所有数据库都支持吗?
不全是。PostgreSQL、SQL Server、Oracle 支持;MySQL 老版本不支持(8.0.31 起支持 INTERSECT/EXCEPT,但仍有历史包袱)。MySQL 上想用交集/差集,用 IN/子查询或 JOIN 模拟,本节的模拟写法可以直接套用。
Q4:集合操作能加 ORDER BY 吗?
能,但 ORDER BY 只能放在最后一条语句末尾,对整个结果集排序。也可以把集合操作整体包一层子查询再排序。注意每条查询内部不能单独排序(会被忽略),除非用括号(部分数据库支持)。
Q5:集合操作和 JOIN 有什么区别?
JOIN 是横向拼接(把列拼起来,行数可能变),集合操作是纵向拼接(把行叠起来,列结构不变)。两者解决完全不同的问题:JOIN 关联多表数据,UNION 合并同类结果。别混淆。
集合操作的概念清晰,但细节容易混,建议动手验证一遍。
准备两张结构相同的表,各自插入几条数据,让两表之间有重复的记录、有各自独有的记录。
第一个练习:用并集合并两表,观察重复记录是否只剩一条;再用不去重的并集合并,观察重复记录保留了几条。对比之下,去重和不去重的差别一目了然。
第二个练习:分别用交集和差集操作,找出两表共有的记录、以及左边有而右边没有的记录。注意差集是有方向的,换个方向结果就不同。
第三个练习:故意让两表的列数不一致,运行并集,观察报错信息,理解"列数必须一致"这条硬性要求。
第四个练习:如果使用的数据库不支持交集和差集,用子查询加存在性判断把它们模拟出来,对比模拟结果和真实操作是否一致。
这四个练习做完,并集去重与否、交集差集的语义、列数要求、以及不支持时的模拟方法,就都清楚了。集合操作在报表合并场景很常用,动手验证过就不会用错。
集合操作把多个查询结果按集合的规则合并:并集去重,交集取共同,差集取独有。需要保留重复时用不去重的并集,列数必须一致,不支持的数据库可以用子查询模拟。