INNER JOIN 只返回双方连接字段均非 NULL 且值相等的行,因 NULL = NULL 结果为 UNKNOWN;LEFT JOIN“漏行”常因错误地将右表过滤条件写入 ON 子句,导致本应保留的左表行对应右表字段全为 NULL。

INNER JOIN 从不返回任何一方为 NULL 的行,这是它的定义决定的,不是 bug,也不是配置问题。
INNER JOIN 的等值条件在 NULL 上永远不成立
SQL 的三值逻辑(TRUE/FALSE/UNKNOWN)决定了:a.id = b.user_id 只要 a.id 或 b.user_id 任一为 NULL,整个表达式结果就是 UNKNOWN,而 INNER JOIN 只保留判定为 TRUE 的行。
- 哪怕左表和右表的连接字段“都是 NULL”,
NULL = NULL仍为UNKNOWN,不会被当作匹配成功 - 所以
INNER JOIN实际上只匹配“双方都非 NULL 且值相等”的行 - 这和你手动写
WHERE a.id IS NOT NULL AND b.user_id IS NOT NULL AND a.id = b.user_id效果一致
为什么 LEFT JOIN 有时也“漏掉”了本该有的 NULL 行
常见错因是把过滤条件误塞进了 ON 子句。比如:
SELECT * FROM orders o LEFT JOIN users u ON o.user_id = u.id AND u.status = 'active'
这个写法会让 u.status != 'active' 或 u.status IS NULL 的用户行在连接结果中全部变成 NULL(即右表字段全空),看起来像“消失”了。
- 正确做法:把右表业务过滤移到
WHERE(但会退化为 INNER JOIN 效果),或保留在ON中但显式允许 NULL:AND (u.status = 'active' OR u.status IS NULL) - 如果目标是“所有订单 + 对应用户(不管状态)”,
ON里就只留连接逻辑:o.user_id = u.id -
LEFT JOIN保证的是左表行不丢,但右表字段是否为 NULL,完全取决于ON条件是否满足
如何验证某列是否真为 NULL,还是压根没连上
别急着用 COALESCE(u.name, 'N/A') 填充——先确认这行是“连上了但 name 是 NULL”,还是“根本没连上(u.* 全为 NULL)”。最直接的办法是查右表主键:
SELECT o.id, u.id AS user_id, u.name FROM orders o LEFT JOIN users u ON o.user_id = u.id
- 若
user_id为NULL,说明没连上,问题出在o.user_id值不存在于users.id,或其中一方为 NULL - 若
user_id有值但name是NULL,才是数据本身缺失,这时COALESCE才有意义 - 用
IS NULL判断比用= NULL安全;IS NOT DISTINCT FROM(PostgreSQL/SQL Server)可让NULL = NULL成立,但仅限特定场景
真正容易被忽略的点是:JOIN 类型选错比 NULL 处理更致命。很多“查不到数据”的问题,根源不在字段值为空,而在用了 INNER JOIN 却需要保左表全量——这时候换 LEFT JOIN 比加一堆 COALESCE 和 OR IS NULL 有用得多。










