LEFT JOIN + IS NULL 是MySQL中查找差集最直接的方式,即找出左表有而右表无匹配的记录,需用ON明确关联条件并在WHERE中判断右表字段IS NULL。

LEFT JOIN + IS NULL 是找差集最直接的方式
MySQL 没有 EXCEPT 或 MINUS,想找出表 A 里有、但表 B 里没有的记录,就得靠 LEFT JOIN 配合 WHERE ... IS NULL。核心逻辑是:左表全量保留,右表匹配不到的字段为 NULL,再把这部分筛出来。
常见错误是写成 WHERE b.id != a.id 或漏掉 IS NULL 判断——这会返回大量错误匹配,甚至空结果。
- 必须用
ON子句明确关联条件(比如ON a.id = b.id),不能只靠WHERE -
WHERE b.id IS NULL必须写在JOIN之后,不能写成AND b.id IS NULL放在ON里(否则语义变成“关联时要求 b.id 为空”,逻辑错乱) - 如果关联字段允许
NULL,要额外注意:NULL = NULL不成立,可能导致漏匹配,建议提前用COALESCE或IS NOT NULL过滤
实际例子:查 orders 表中没出现在 order_items 中的订单
假设 orders 表有 order_id,order_items 表也存了 order_id;目标是找出那些创建了但还没加任何商品的订单。
SELECT o.order_id, o.created_at FROM orders o LEFT JOIN order_items oi ON o.order_id = oi.order_id WHERE oi.order_id IS NULL;
这里关键点:oi.order_id IS NULL 表示该订单在 order_items 中完全没对应行。如果误写成 WHERE oi.order_id != o.order_id,结果会为空或错乱——因为未匹配时 oi.order_id 是 NULL,而 NULL != anything 永远为 UNKNOWN,被当作 FALSE 过滤掉。
性能和索引注意事项
差集查询容易慢,尤其当右表很大时。LEFT JOIN 仍会尝试对每行左表去右表找匹配,没索引就全表扫描。
- 确保
ON中用到的右表字段(如order_items.order_id)有索引,最好是单独索引或联合索引的最左前缀 - 避免在
ON或WHERE中对右表字段做函数操作(如ON a.id = COALESCE(b.id, 0)),会导致索引失效 - 如果只是判断“是否存在”,用
NOT EXISTS可能更高效(尤其右表数据量远大于左表时),但语义等价、写法不同:SELECT * FROM orders o WHERE NOT EXISTS (SELECT 1 FROM order_items oi WHERE oi.order_id = o.order_id)
多个字段联合判断差集时怎么写
当差集依赖多个字段(比如既要 user_id 相同,又要 status 相同),ON 条件必须全部写上,且任一字段不匹配都会导致右表整行为 NULL。
SELECT a.* FROM table_a a LEFT JOIN table_b b ON a.user_id = b.user_id AND a.status = b.status AND a.category = b.category WHERE b.user_id IS NULL;
注意:这里检查 b.user_id IS NULL 就够了,不用每个字段都判 IS NULL——只要一行没匹配上,所有 b.xxx 都是 NULL。但如果 b.user_id 允许存 NULL 值(业务上合法),那就要改用 b.user_id IS NULL AND b.status IS NULL AND b.category IS NULL,否则可能把“右表真有 NULL 字段的匹配行”误判为差集。
多字段场景下最容易忽略的是 NULL 安全性,别默认字段非空。


















