1. LEFT JOIN + IS NULL(推荐)
语法
sql
SELECT a.*
FROM a
LEFT JOIN b ON a.id = b.id
WHERE b.id IS NULL;
特点
-
原理:通过左连接保留A表所有记录,筛选B表中无匹配的记录(即B.id为NULL)。
-
优点:
-
执行效率高(尤其在B.id有索引时)。
-
对NULL值安全,无需额外处理。
-
-
适用场景:大数据量、需返回A表全部字段。
2. NOT IN子查询(谨慎使用)
语法
sql
SELECT a.*
FROM a
WHERE a.id NOT IN (SELECT DISTINCT b.id FROM b);
特点
-
原理:筛选A表中不在B表id集合中的记录。
-
缺点:
-
若B.id存在NULL值,查询会返回空结果(因
NOT IN (NULL, ...)
逻辑失效)。 -
子查询结果集大时性能较差。
-
-
改进方案:
sql
-- 显式排除NULL值 SELECT a.* FROM a WHERE a.id NOT IN (SELECT b.id FROM b WHERE b.id IS NOT NULL);
3. 集合操作符(MINUS/EXCEPT)
语法
sql
-- Oracle/PostgreSQL
SELECT a.id FROM a
MINUS
SELECT b.id FROM b;
-- SQL Server
SELECT a.id FROM a
EXCEPT
SELECT b.id FROM b;
特点
-
原理:直接取A表与B表的id差集。
-
优点:语法简洁,适合仅需id字段的场景。
-
缺点:
-
仅返回id字段,无法获取A表其他字段。
-
不同数据库语法不同(如SQL Server用
EXCEPT
)。
-
4. NOT EXISTS子查询(推荐)
语法
sql
SELECT a.*
FROM a
WHERE NOT EXISTS (SELECT 1 FROM b WHERE a.id = b.id);
特点
-
原理:逐行检查A表记录是否在B表中存在。
-
优点:
-
对NULL值安全,无需过滤。
-
性能优化潜力大(若B.id有索引,效率接近LEFT JOIN)。
-
-
适用场景:需要返回A表全部字段,且B表有索引。
性能对比与选择建议
方法 | 优点 | 缺点 | 推荐场景 |
---|---|---|---|
LEFT JOIN | 高效,支持全字段返回 | 需明确关联条件 | 通用场景,大数据量 |
NOT IN | 语法简单 | NULL值敏感,子查询性能差 | B表无NULL且数据量小 |
MINUS/EXCEPT | 简洁,适合单字段 | 仅返回id字段,跨数据库语法差异 | 仅需id字段 |
NOT EXISTS | 对NULL安全,支持索引优化 | 语法稍复杂 | B表有索引或需逐行检查逻辑 |
示例验证
数据准备
sql
-- A表
CREATE TABLE a (id INT, name VARCHAR(10));
INSERT INTO a VALUES (1, 'A1'), (2, 'A2'), (3, 'A3');
-- B表
CREATE TABLE b (id INT);
INSERT INTO b VALUES (2), (NULL);
查询结果
-
LEFT JOIN:返回
(1, 'A1'), (3, 'A3')
-
NOT IN:若未过滤NULL,返回空;若过滤NULL,返回
(1, 'A1'), (3, 'A3')
-
MINUS:返回
1, 3
-
NOT EXISTS:返回
(1, 'A1'), (3, 'A3')
总结
-
首选方案:
LEFT JOIN
或NOT EXISTS
,兼顾性能与安全性。 -
避坑指南:
-
避免直接使用
NOT IN
处理含NULL值的子查询。 -
使用集合操作符时注意字段限制和数据库兼容性。
-
-
优化关键:为关联字段(如
id
)建立索引,定期更新统计信息。