从没下过单的客户:反连接的三种写法
LeetCode 183 · LEFT JOIN + IS NULL、NOT EXISTS、NOT IN 的陷阱
面试怎么回答
这是反连接。我优先用 NOT EXISTS 或 LEFT JOIN ... WHERE o.Id IS NULL;不太用 NOT IN,因为子查询里只要出现 NULL,NOT IN 的结果就全是未知,会一行都查不出来,这是面试里常被追问的坑。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
题意
有客户表和订单表,订单表记录了下单客户的 ID。找出一次都没下过单的客户姓名。
表结构
- Customers:Id(主键)、Name
- Orders:Id(主键)、CustomerId
思路
「在 A 里但不在 B 里」叫反连接。最常用的是 LEFT JOIN 后取右表主键为 NULL 的行;也可以写 NOT EXISTS。NOT IN 也能写,但如果子查询里有 NULL,整个条件会变成未知,一行都查不出来。
参考解法
select c.Name Customers
from Customers c left join Orders o
on c.id = o.CustomerId
where o.id is null
另一种写法:NOT EXISTS,语义最直接,也不受 NULL 影响
SELECT c.Name AS Customers
FROM Customers c
WHERE NOT EXISTS (SELECT 1 FROM Orders o WHERE o.CustomerId = c.Id)
易错点
- 用
NOT IN (SELECT CustomerId FROM Orders)时,只要 CustomerId 有一个 NULL,结果就是空集。 - LEFT JOIN 后判断空要判断右表的非空列(比如主键),不要判断可能本来就为空的列。