🎯 深入理解:JOIN 中 ON vs WHERE 条件差异

很多人在写 SQL 时,会混用 ONWHERE 限制条件。但这并不是无关紧要的"风格"问题,尤其在 OUTER JOIN(如 LEFT JOIN)中,放错地方逻辑就彻底变了


1️⃣ 基础:ON 做"配对",WHERE 做"筛选"

  • ON 子句:定义两个表之间"如何配对"的逻辑。
  • WHERE 子句:在配对完成后对结果进行过滤,决定哪些行最终返回。

正如社区大佬所说:

"The ON clause defines the relationship between the tables. The WHERE clause describes which rows you are interested in." stackoverflow.com+14stackoverflow.com+14geeksforgeeks.org+14


2️⃣ INNER JOIN:语义一致,差距仅在"表达方式"

对于 INNER JOIN(内连接),无论你把条件放在 ON 还是 WHERE,结果集最终都一样------这是数学上的布尔逻辑统一:

css 复制代码
-- a) 条件放在 ON
SELECT *
FROM A
JOIN B ON A.id = B.id AND B.status = 'active';

-- b) 条件放在 WHERE
SELECT *
FROM A
JOIN B ON A.id = B.id
WHERE B.status = 'active';

Percona 专家总结:

"No, there's no difference. ... MySQL will have the same execution plan." reddit.com+3percona.com+3bennadel.com+3


3️⃣ OUTER JOIN(如 LEFT JOIN):逻辑大崩盘!

这里是关键!不当使用就可能把含 NULL 的行"干掉",从 LHS 表漏掉记录。

👇 场景 A:想保留左表所有行,并只在右表满足条件时才关联

ini 复制代码
SELECT u.name, h.info
FROM users u
LEFT JOIN houses h
  ON u.id = h.owner_id
   AND h.status = 'rented';
  • 执行流程

    1. 先取 users 表整表扫描;
    2. 每行尝试在 houses 表中配对 owner_id + status;
    3. 如果不满足 status = 'rented',仍保留 users 行,但 houses 字段为 NULL
      ✅ 保证"users 全保、houses 有条件过滤"

来自 Kateryna 的清晰说明:

"when the filter is on ON clause, ... dept table is filtered before being left joined ... NULL entries ..." blog.jooq.org+2en.wikipedia.org+2mode.com+2dba.stackexchange.com+10pluralsight.com+10geeksforgeeks.org+10blog.jooq.org+2learnsql.com+2dba.stackexchange.com+2

🔥 场景 B:把条件挪到 WHERE------逻辑彻底变了!

ini 复制代码
SELECT u.name, h.info
FROM users u
LEFT JOIN houses h
  ON u.id = h.owner_id
WHERE h.status = 'rented';
  • 这时候 SQL 语义变成:

    1. 先完全关联;
    2. 然后丢弃 h.status != 'rented' 的行;
    3. 最可怕的是------那些 houses=NULL 的行,也会被一起剔除。
      结果就是 Left Join 切成 Inner Join

正如 dev.to 上所述:

"this filter ... filter rows after the join ... effectively an inner join in disguise" blog.jooq.orgdev.to+1dev.to+1dba.stackexchange.com+2pluralsight.com+2geeksforgeeks.org+2


4️⃣ 性能差异?

  • INNER JOIN 条件放哪不会影响执行计划;
  • LEFT JOIN 时,把限制尽量放 ON 中能让 SQL 引擎少输出 NULL 行再过滤,在大数据场景下性能更优 w3schools.com+14percona.com+14blog.jooq.org+14
  • 但很多优化器智能到把顺序重组,主流 MySQL/Percona 通常无明显差异 percona.com

5️⃣ 心智模型 & 记忆锚

JOIN 类型 ON 里加条件 WHERE 里加条件
INNER JOIN ✅ 同效果,推荐在 ON 放条件,提高可读性 ✅ Late filter,清晰逐位"配对"后再筛选
LEFT JOIN ✅ 想保留左表全量结果时,请把条件放 ON ❌ 条件放 WHERE,等于转成 INNER JOIN,漏掉 NULL

口诀

  • JOIN 用 ON,配对优先
  • 条件用 WHERE,事后筛选
  • 想要 Outer Join 保数据?条件要写在 ON!

6️⃣ 实战建议

  • INNER JOIN:条件语义清晰,放 ON 更能"一眼明白"。

  • LEFT JOIN

    • 保左删右ON u.id = h.uid AND h.flag='ok'
    • 啥都保但最终筛条件 :使用 WHERE ,注意 NULL 行可能被删。

✨ 总结:别再"套用模板",Think before you write

  • ON = 连接逻辑,决定两表如何组合。
  • WHERE = 全表过滤,决定最后哪些行被展示。
  • 错用会让语句"看着左连接,结果像内连接",逻辑漏洞埋在细节里。
相关推荐
fouryears_234171 小时前
适配器模式——以springboot为例
java·spring boot·适配器模式
汽车功能安全啊2 小时前
利用对称算法及非对称算法实现安全启动
java·开发语言·安全
paopaokaka_luck2 小时前
基于Spring Boot+Vue的吉他社团系统设计和实现(协同过滤算法)
java·vue.js·spring boot·后端·spring
Flobby5293 小时前
Go语言新手村:轻松理解变量、常量和枚举用法
开发语言·后端·golang
Jonariguez3 小时前
Mysql InnoDB存储引擎
数据库·mysql
Warren984 小时前
Java Stream流的使用
java·开发语言·windows·spring boot·后端·python·硬件工程
程序视点5 小时前
IObit Uninstaller Pro专业卸载,免激活版本,卸载清理注册表,彻底告别软件残留
前端·windows·后端
xidianhuihui5 小时前
go install报错: should be v0 or v1, not v2问题解决
开发语言·后端·golang
架构师沉默5 小时前
Java优雅使用Spring Boot+MQTT推送与订阅
java·开发语言·spring boot
tuokuac5 小时前
MyBatis 与 Spring Boot版本匹配问题
java·spring boot·mybatis