首页/数据/SQL 速查/ON 与 WHERE 在外连接中的区别

ON 与 WHERE 在外连接中的区别

内连接里 ON 与 WHERE 可以互换;外连接中不能:ON 决定配对,WHERE 在配对结果上过滤,写在 WHERE 会滤掉补 NULL 的行。

SQL 速查数据更新于 2026-10-13

ON 与 WHERE 的区别是什么

内连接里 ON 与 WHERE 可以互换,优化器通常会把它们合并处理;外连接里不能互换:ON 决定两表如何配对,WHERE 在配对结果上过滤。把右表条件写进 WHERE,会把右表补 NULL 出来的行一并过滤掉,外连接静默退化成内连接,数据变少却不报错。左表条件写在 ON 还是 WHERE 结果相同,但写在 WHERE 语义更清楚。多表外连接串联时,每一步的 ON 与 WHERE 位置都要单独确认。

基本信息

内连接ON 与 WHERE 语义等价,写法可互换
外连接 ON决定配对规则;右表未匹配的行仍保留并补 NULL
外连接 WHERE在配对结果上过滤,会删掉补 NULL 的行
左表条件写 ON 或 WHERE 结果相同,WHERE 语义更清晰
多表串联每一级外连接的右表条件都要放在对应的 ON 里
适用平台标准 SQL 语义,MySQL 8.0 与 PostgreSQL 一致

典型使用场景

  • 外连接限定匹配范围:只匹配已支付订单,但仍保留所有用户
  • 外连接过滤左表:只要某地区的用户,条件写 WHERE
  • 排查外连接退化:结果行数比左表还少时,先看 WHERE 里有没有右表条件
  • 多级左连接:A 左连 B 再左连 C,B 与 C 的过滤条件分别放各自的 ON
  • 反连接:WHERE 右表主键 IS NULL 专门保留未匹配行
  • 报表补零:日期维表左连事实表,事实侧的过滤条件必须写在 ON

两种语义对比

两种语义对比
-- 语义 A:只匹配已支付订单,但保留所有用户(条件写在 ON)
SELECT u.id, o.amount
FROM users u
LEFT JOIN orders o ON o.user_id = u.id AND o.status = 'paid';

-- 语义 B:只要下过已支付订单的用户(条件写在 WHERE,退化为内连接)
SELECT u.id, o.amount
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.status = 'paid';

-- 左表条件写在 WHERE,不影响外连接语义
SELECT u.id, o.amount
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE u.city = 'Beijing';

条件位置对照

场景写在 ON写在 WHERE
右表条件(外连接)保留左表全部行,未匹配补 NULL补 NULL 的行被丢弃,退化为内连接
左表条件(外连接)与写在 WHERE 结果相同语义更明确,推荐
连接之外的非等值附加条件常用,用于收窄匹配范围等价,可读性略差
内连接的两表条件与 WHERE 等价与 ON 等价
反连接判断不能实现WHERE 右表主键 IS NULL
判断写法是否正确有一个快检:外连接的结果行数不应该少于左表行数,比左表还少就说明右表条件写错了位置。
右表条件写在 WHERE 是外连接最常见的错误:结果静默变成内连接,数据少了也不报错,排查时先对比两边行数。
把连接条件与过滤条件分开写:ON 只写两表之间的关系,WHERE 只写结果集要满足的条件,可读性最好,也最不容易改错。
USING(col) 是 ON a.col = b.col 的简写,结果里该列只出现一次;跨库复用时注意部分数据库对 USING 的支持差异。

实践要点

  • 写外连接时先问一句:这张表的过滤是要收窄匹配,还是要筛掉结果行
  • 收窄匹配放 ON,筛结果放 WHERE,两者混写是 bug 的高发区
  • 多级外连接逐段验证行数,每一步都确认没有意外过滤或放大
  • 用注释标明 ON 里条件的意图,避免后人把它挪到 WHERE
  • 结果行数异常时先数左表行数,再数连接后的行数
  • 反连接优先用 NOT EXISTS,语义不受 NULL 影响
三张表以上的连接里判断条件属于谁,看它引用了哪张表的列:只引用右表的过滤条件放 ON,引用多表的比较条件本来就是连接条件的一部分。

易错点

  • 右表过滤条件写在 WHERE,外连接退化为内连接,数据少了却不报错
  • 把 ON 里的收窄条件误当成冗余而删掉,匹配范围被放大
  • 多级外连接时把后一级的条件写在前一级的 ON 里,结果与预期不符
  • 用外连接加 WHERE 右表列 IS NULL 做反连接却写成 IS NOT NULL,意思正好相反
  • 在 ON 里写只跟左表相关的条件,语义含糊,后续维护者容易误改

数据来源与参考

  • MySQL 8.0 Reference Manual · LEFT JOIN
  • PostgreSQL Documentation · Joined Tables
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌