left join + where 右表字段 is null 能查出左表有而右表无的记录,因left join保留左表全部行、右表无匹配时字段为null,用右表可空字段(如主键)判null即可筛选;须避免用= null或误将右表过滤条件写入where。

LEFT JOIN + WHERE IS NULL 为什么能查出不匹配记录
因为 LEFT JOIN 会保留左表所有行,右表无匹配时对应字段全为 NULL;再用 WHERE 右表主键 IS NULL 就能筛出“左表有、右表无”的记录。关键点是:必须用右表的**可空字段**(通常是主键或外键)判断 NULL,不能用左表字段——否则可能误过滤。
常见错误:WHERE 右表.某非空默认值字段 = NULL(永远不成立,= NULL 无效),或漏写 IS NULL 写成 = NULL。
- 正确写法:
WHERE t2.id IS NULL(假设t2.id是右表主键) - 错误写法:
WHERE t2.name = NULL或WHERE t2.id = NULL - 如果右表该字段被定义为
NOT NULL,优先选它;否则选明确不会被设为NULL的外键字段
INNER JOIN 和 LEFT JOIN 在不匹配场景下的行为差异
INNER JOIN 只返回两表都有匹配的记录,天然无法查“不匹配”;而 LEFT JOIN 是唯一能直接支撑“查缺失”的标准方式。有人试图用 RIGHT JOIN,但可读性差且容易混淆左右逻辑——统一用 LEFT JOIN 并把“要保全的表”放在左边更稳妥。
-
INNER JOIN:结果集是交集,不满足“查不匹配”需求 -
LEFT JOIN ... WHERE 右表字段 IS NULL:查左表独有记录 - 想查右表独有?把右表放左边,改用
LEFT JOIN+WHERE 左表字段 IS NULL -
FULL OUTER JOIN虽能查两边独有,但 MySQL 不支持,PostgreSQL/SQL Server 才可用
性能陷阱:WHERE IS NULL 会不会让索引失效
会,但只在特定条件下。如果右表用于 JOIN 的字段有索引,LEFT JOIN 阶段仍能走索引;但后续 WHERE t2.id IS NULL 这一步,优化器通常仍能利用索引快速定位空值(尤其当该字段有 NOT NULL 约束时,IS NULL 实际是常量过滤)。真正拖慢的是:右表字段没索引、或 JOIN 条件本身没走索引。
- 确保
ON子句中右表的关联字段有索引(如t2.user_id) - 避免在
WHERE中对右表字段做函数操作,例如WHERE COALESCE(t2.id, 0) = 0—— 索引必然失效 - 大表慎用:先
EXPLAIN看是否用了type: ref或range,而非ALL
MySQL 8.0+ 中 NOT EXISTS 是否比 LEFT JOIN 更优
语义等价,但执行计划可能不同。多数情况下两者性能接近;NOT EXISTS 在右表数据量极大、且存在早期终止条件时略占优势(找到一个匹配就跳过剩余扫描),而 LEFT JOIN 必须完成全部关联。不过可读性上,LEFT JOIN ... IS NULL 更直观,也更易调试——你可以临时去掉 WHERE 子句看中间结果。
- 推荐写法:
SELECT t1.* FROM t1 LEFT JOIN t2 ON t1.id = t2.t1_id WHERE t2.t1_id IS NULL - 等价
NOT EXISTS:SELECT * FROM t1 WHERE NOT EXISTS (SELECT 1 FROM t2 WHERE t2.t1_id = t1.id) - 如果子查询里加了
WHERE过滤(比如t2.status = 'active'),NOT EXISTS更自然;而LEFT JOIN需把该条件移到ON里,否则会变成内连接逻辑
实际写的时候,最易忽略的是 ON 和 WHERE 的分工:过滤右表的条件必须写进 ON,而不是 WHERE,否则就从“查不匹配”悄悄变成了“查匹配后过滤”。










