很多人在写 SQL 时,会混用 ON
和 WHERE
限制条件。但这并不是无关紧要的"风格"问题,尤其在 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';
-
执行流程:
- 先取
users
表整表扫描; - 每行尝试在
houses
表中配对owner_id + status
; - 如果不满足
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 语义变成:
- 先完全关联;
- 然后丢弃
h.status != 'rented'
的行; - 最可怕的是------那些
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 = 全表过滤,决定最后哪些行被展示。
- 错用会让语句"看着左连接,结果像内连接",逻辑漏洞埋在细节里。