4.4 集合操作 UNION/INTERSECT/EXCEPT


4.4 集合操作 UNION/INTERSECT/EXCEPT

本节摘要:集合操作把多个查询结果按集合规则合并。UNION 并集、INTERSECT 交集、EXCEPT 差集。本节讲它们的用法和 UNION 与 UNION ALL 的区别。

先说结论

阅读完本节,你应当能够:

  1. 用 UNION 合并结果
  2. 区分 UNION 和 UNION ALL
  3. 用 INTERSECT 取交集、EXCEPT 取差集
  4. 理解集合操作的列匹配规则

一、三个集合操作

集合操作把两个查询结果按集合规则合并,要求两查询的列数和类型对应:

  • UNION:并集(两个结果合一起,去重)
  • UNION ALL:并集但不去重
  • INTERSECT:交集(两边都有的)
  • EXCEPT(部分 DBMS 叫 MINUS):差集(左边有、右边没有的)
-- 北京和上海的客户合一起 SELECT * FROM Customers WHERE City = '北京' UNION SELECT * FROM Customers WHERE City = '上海';

二、UNION vs UNION ALL

操作 去重 性能
UNION 去重 慢(要排序去重)
UNION ALL 不去重
-- UNION ALL:保留重复,快 SELECT Amount FROM Orders WHERE CustomerID = 101 UNION ALL SELECT Amount FROM Orders WHERE CustomerID = 102;

💡 关键直觉:确定没重复或要保留重复时用 UNION ALL,它省去重开销快很多。只有需要去重才用 UNION。

三、INTERSECT 和 EXCEPT

-- 既有订单又活跃的客户(交集) 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; -- 两列,类型对应即可

五、用 JOIN/IN 模拟

不支持 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 合并时忘了列数要相同,报错。集合操作两查询的列数和类型必须对应,结果列名取第一个。

温故知新

  • UNION:并集去重;UNION ALL:并集不去重,更快,确定无重复或要保留重复时用它。
  • INTERSECT:交集;EXCEPT(Oracle 叫 MINUS):差集,不是所有 DBMS 支持。
  • 列匹配:两查询列数相同、类型兼容、结果列名取第一个。
  • 模拟:不支持时用 IN/NOT IN/JOIN 模拟 INTERSECT/EXCEPT。
  • 集合操作适合合并结构相同的结果集,去重需求决定 UNION 还是 UNION ALL。

第 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 合并同类结果。别混淆。

动手做一做

集合操作的概念清晰,但细节容易混,建议动手验证一遍。

准备两张结构相同的表,各自插入几条数据,让两表之间有重复的记录、有各自独有的记录。

第一个练习:用并集合并两表,观察重复记录是否只剩一条;再用不去重的并集合并,观察重复记录保留了几条。对比之下,去重和不去重的差别一目了然。

第二个练习:分别用交集和差集操作,找出两表共有的记录、以及左边有而右边没有的记录。注意差集是有方向的,换个方向结果就不同。

第三个练习:故意让两表的列数不一致,运行并集,观察报错信息,理解"列数必须一致"这条硬性要求。

第四个练习:如果使用的数据库不支持交集和差集,用子查询加存在性判断把它们模拟出来,对比模拟结果和真实操作是否一致。

这四个练习做完,并集去重与否、交集差集的语义、列数要求、以及不支持时的模拟方法,就都清楚了。集合操作在报表合并场景很常用,动手验证过就不会用错。

一句话记忆

集合操作把多个查询结果按集合的规则合并:并集去重,交集取共同,差集取独有。需要保留重复时用不去重的并集,列数必须一致,不支持的数据库可以用子查询模拟。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U