eloquent 的 leftjoin() 才真正对应 sql 的 left join,保留左表所有记录,右表无匹配时字段为 null;常见错误是误用 join() 导致内连接、混淆 on() 与 where()、未处理字段重名或 null 判断不当。

Left Join 语法写法必须用 leftJoin(),不是 join() 或 rightJoin()
很多人写成 join() 后发现右表没数据的行直接消失了,其实是用了内连接。Eloquent 的 leftJoin() 才真正对应 SQL 的 LEFT JOIN,它会保留左表(主模型)所有记录,右表字段为 null 时也照常返回。
常见错误现象:join() 写完一查,用户列表变少了——那是因为只返回了有对应订单的用户;而你要的是“所有用户,不管有没有订单”。
-
leftJoin()第一个参数是右表名(字符串),第二个是 ON 条件(闭包或字符串) - 条件里要用
on()方法写关联逻辑,不能用where(),否则会变成 WHERE 过滤,导致左表被意外裁剪 - 如果右表字段和左表重名(比如都叫
id),记得用select()显式指定别名,否则 Eloquent 可能覆盖掉左表字段
关联字段为空时,get() 返回的集合里右表属性确实是 null
这是 Left Join 的本质行为,但新手常误以为“没查到就是报错”或“应该自动补默认值”。Eloquent 不会帮你填充,也不会抛异常——它老老实实把 SQL 返回的 null 塞进结果对象的对应属性里。
使用场景:查用户列表并附带最新一条地址记录,地址不存在就留空,不中断整个查询。
- 检查右表字段是否为
null要用=== null,避免被empty()或!$value误判(比如地址是空字符串、0、false 也会被当成“无”) - 如果想统一处理缺失值,可以在
selectRaw()里用COALESCE(address.city, '未知'),但注意这属于 SQL 层面处理,PHP 端收不到原始null -
with()预加载不等价于leftJoin():前者发多条 SQL,后者是一条,且只有后者能跨表筛选(比如 “查所有用户,但只显示有北京地址的用户” —— 这时要在on()里写条件,不是where())
多个 Left Join 顺序和别名容易引发字段混淆
连三张表时,第二、第三个 leftJoin() 的 ON 条件如果还写成 users.id = posts.user_id,MySQL 会报 “Unknown column”,因为前一个 join 已经让查询上下文变了。必须明确每步的左端是谁。
性能影响:左表小、右表大时,加索引在右表的关联字段(如 posts.user_id)上很关键;否则可能全表扫描右表 N 次(N 是左表记录数)。
- 给每个右表起别名(如
leftJoin('posts as p', ...)),然后在select()和on()里统一用别名,避免歧义 - 不要在同一个查询里混用
join()和leftJoin(),除非你清楚执行顺序和结果集语义——MySQL 按书写顺序解析,join会把前面 left join 的结果再内连接,可能意外丢掉左表记录 - 调试时用
toSql()看生成的 SQL,比猜强得多:User::query()->leftJoin(...)->toSql()
用 leftJoin() 做条件过滤时,ON 和 WHERE 的区别必须分清
这是最常踩的坑。例如:“查所有用户,但只关联状态为 active 的订单”。如果把 status = 'active' 写在 where(),那就变成了“查有 active 订单的用户”,左表就被过滤了;写在 on() 里,才是“查所有用户,并只尝试关联 active 的订单”,没匹配上的用户依然保留。
-
on()里的条件控制“哪些右表行能连上来”,不影响左表行数 -
where()是对最终结果集过滤,一旦涉及右表字段,就会剔除右表为null的那些左表行 - 实在拿不准,就把条件拆开:先用
on()控制连接逻辑,再用when()动态加where()判断右表字段是否存在
leftJoin() 行为完全取决于你写的 on() 条件和后续的 where() 位置,没有隐藏逻辑。只要 SQL 层面对应得准,PHP 层就不会出人意料。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











