在 SQL 中LEFT JOIN和INNER JOIN后面跟上ON条件、AND条件以及WHERE条件它们的行为逻辑有着本质的区别。理解这个区别是写出无 Bug SQL 的关键。核心一句话总结ON决定“如何连接”WHERE决定“要哪些行”。对于LEFT JOINON中的条件永远不丢弃左表数据而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.idB.id AND B.type1确保左表不丢失。针对“左表”的限制条件如只要姓张的用户写在WHERE后面例如WHERE A.name LIKE 张%因为左表为空时应该丢弃该用户。针对“右表”的限制条件且不需要左表的空数据直接改用INNER JOIN写在ON或WHERE均可既然左表空数据不要了用INNER JOIN语义更清晰性能也更好。通用的业务硬性过滤如时间范围、状态码写在WHERE后面这是对最终返回结果的统一筛选维护起来最直观。终极口诀关联条件上ON过滤右表加AND过滤整体放WHERE左表数据想保全右表过滤别放WHERE