SQL中寻找两个集合不交集的最简洁高效方法是什么?
当然有更简洁高效的写法!先帮你理清楚需求:你要找的是两个表中仅出现在其中一个表的记录,也就是集合论里的「对称差集」。先把你的示例用清晰格式列出来:
示例表结构
表 T1:
| Val |
|---|
| 1 |
| 2 |
| 4 |
| 5 |
表 T2:
| Val |
|---|
| 1 |
| 3 |
| 4 |
| 5 |
| 6 |
预期结果
| Val |
|---|
| 2 |
| 3 |
| 6 |
先提个小问题:你原来的CTE写法其实有逻辑漏洞哦——LEFT JOIN之后判断Val IS NOT NULL根本筛不出只在单边存在的记录,反而会返回所有非空的Val,实际运行结果会不符合预期。下面给你几种更靠谱的写法:
方法1:用UNION ALL结合EXCEPT(适用于SQL Server、PostgreSQL等支持的数据库)
这是最简洁的写法,直接利用数据库的集合操作能力:
-- 先取T1独有的记录,再取T2独有的记录,合并结果 SELECT Val FROM T1 EXCEPT SELECT Val FROM T2 UNION ALL SELECT Val FROM T2 EXCEPT SELECT Val FROM T1
EXCEPT会自动去重并筛选出只在左表存在的记录,因为两个结果集没有重叠,用UNION ALL比UNION更高效(不用额外去重)。
方法2:用FULL OUTER JOIN(通用大部分SQL数据库)
如果你的数据库支持全外连接,这种写法性能也很出色:
SELECT COALESCE(T1.Val, T2.Val) AS Val FROM T1 FULL OUTER JOIN T2 ON T1.Val = T2.Val -- 筛选出其中一边没有匹配的行 WHERE T1.Val IS NULL OR T2.Val IS NULL
FULL OUTER JOIN会返回两个表的所有记录,匹配的行两边都有值,不匹配的行其中一边为NULL。我们只需要把这些单边为空的行挑出来,用COALESCE取非空的Val即可。
方法3:通用写法(兼容所有SQL数据库)
如果你的数据库不支持EXCEPT或FULL OUTER JOIN,可以用这种万能写法:
SELECT Val FROM ( -- 合并两个表的所有记录 SELECT Val FROM T1 UNION ALL SELECT Val FROM T2 ) AS combined -- 分组后只保留出现1次的Val(仅在一个表存在) GROUP BY Val HAVING COUNT(*) = 1
这种写法兼容性拉满,但如果数据量很大,分组统计的开销会比前两种方法高一些,适合小数据量或兼容性要求极高的场景。
效率对比
- 方法1和方法2在
Val字段有索引时效率最高,数据库可以直接利用索引做集合比对或连接操作。 - 方法3虽然通用,但大数据量下性能不如前两者。
内容的提问来源于stack exchange,提问作者user7127000
相关产品推荐
相关产品推荐

