left join 和inner join on 后边的条件 还有 and 条件 和where 后的比较
在 SQL 中,LEFT JOIN和INNER JOIN后面跟上ON条件、AND条件以及WHERE条件,它们的行为逻辑有着本质的区别。理解这个区别是写出无 Bug SQL 的关键。
核心一句话总结:ON决定“如何连接”,WHERE决定“要哪些行”。对于LEFT JOIN,ON中的条件永远不丢弃左表数据;而WHERE中的条件会。
下面我分两种连接类型,为你彻底拆解:
1. 对于 INNER JOIN:两者逻辑等价(但推荐写 ON)
在INNER JOIN(内连接)中,无论是把条件写在ON后面、AND后面,还是WHERE后面,最终结果和性能执行计划几乎完全一样。
sql
-- 这三种写法结果完全一致 SELECT * FROM A INNER JOIN B ON A.id = B.id AND B.status = 1; SELECT * FROM A INNER JOIN B ON A.id = B.id WHERE B.status = 1; SELECT * FROM A INNER JOIN B ON A.id = B.id AND A.age > 18 WHERE B.status = 1;
原因:INNER JOIN 只取两表的交集,任何一方不满足条件,整行都会被丢弃。
最佳实践:虽然位置不影响结果,但为了可读性和维护性,强烈建议:
ON后面只放表与表之间的关联条件(如A.id = B.id)。WHERE后面放业务过滤条件(如B.status = 1)。这样代码逻辑清晰,一眼能看出什么是连接关系,什么是业务筛选。
2. 对于 LEFT JOIN:这是本质区别(极易踩坑)
LEFT JOIN以左表为准,无论如何,左表的数据都会全部返回。此时,ON/AND和WHERE产生了分水岭般的作用。
场景 A:条件放在ON或AND中(不影响左表行数)
这里的条件(非关联条件)是在生成临时结果集之前,专门用于过滤右表的。
效果:左表数据全部保留,右表只有满足
AND条件的行被连接上;如果不满足,右表字段填NULL。示例:
sql
-- 需求:查出所有用户,并尝试查出他们状态为 1 的订单 SELECT u.id, u.name, o.order_no FROM users u LEFT JOIN orders o ON u.id = o.user_id AND o.status = 1;
结果:张三(无订单)会返回NULL;李四(有订单但状态=0)会返回NULL;王五(有订单且状态=1)显示订单号。左表用户一条都不会少。
场景 B:条件放在WHERE中(可能把 LEFT JOIN 变成 INNER JOIN)
这里的条件是在临时结果集生成之后,对整体结果进行最终过滤。
效果:只要右表的字段不满足
WHERE条件,并且右表字段为NULL,那么这一整行(包括左表数据)都会被彻底丢弃。示例:
sql
-- 需求:查出所有用户,并且只要状态为 1 的订单(错误写法,会丢用户) SELECT u.id, u.name, o.order_no FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE o.status = 1;
结果:张三(无订单,o.status IS NULL)因为不满足WHERE o.status = 1,整行被丢弃,导致查不到没有符合条件的订单的用户。这实际上等效于INNER JOIN。
3. 执行顺序与性能影响(数据库视角)
SQL 标准的逻辑执行顺序为:FROM→ON→JOIN→WHERE→SELECT。
ON阶段(含 AND):发生在连接过程中。数据库会根据ON条件,决定如何从右表匹配数据。对于LEFT JOIN,右表数据在此时就被筛选掉了,这能减少参与连接的数据量,提升性能。WHERE阶段:发生在连接完成后。此时数据行已经确定,WHERE是对最终结果集的“清洗”。
性能陷阱警告:如果你在
LEFT JOIN的WHERE中过滤了右表的非空字段,数据库优化器会识别出“右表不能为 NULL”,从而自动将LEFT JOIN重写为INNER JOIN。虽然执行计划可能变快,但业务逻辑完全变了(数据丢了),这是最致命的隐藏 Bug。
4. 终极实战建议(记住这张表)
| 你的业务需求 | 条件应该写在哪 | 为什么 |
|---|---|---|
表与表的关联键(如A.id = B.a_id) | 写在ON后面 | 这是连接的基本契约,必须放这里。 |
| 针对“右表”的限制条件(且必须保留左表全部数据) | 写在ON后面(用AND) | 例如:LEFT JOIN B ON A.id=B.id AND B.type=1,确保左表不丢失。 |
| 针对“左表”的限制条件(如只要姓张的用户) | 写在WHERE后面 | 例如:WHERE A.name LIKE '张%',因为左表为空时应该丢弃该用户。 |
| 针对“右表”的限制条件(且不需要左表的空数据) | 直接改用INNER JOIN,写在ON或WHERE均可 | 既然左表空数据不要了,用INNER JOIN语义更清晰,性能也更好。 |
| 通用的业务硬性过滤(如时间范围、状态码) | 写在WHERE后面 | 这是对最终返回结果的统一筛选,维护起来最直观。 |
终极口诀:
关联条件上
ON,过滤右表加AND,过滤整体放WHERE,左表数据想保全,右表过滤别放WHERE!