Laravel 9中使用with()引发多查询,是否应改用join?
问题解答
首先纠正你现有代码的两个问题:
with()方法的参数写法错误,应该把嵌套关联用点语法串联,或者放在同一个数组结构里;- 筛选"John"的条件应该直接写在
Person的查询上,你写的['person']关联完全没必要——因为本身就是查询Person模型。
回到你的核心问题:要不要改用join?这取决于你的使用场景,下面是两种方案的对比和代码示例:
方案一:优化Eloquent预加载(推荐用于需要完整模型实例的场景)
Laravel的预加载是解决N+1查询的标准方案,你之前的错误写法导致了多余查询,正确的嵌套预加载会用批量whereIn来获取关联数据,效率并不低:
// 正确写法:一次性获取John及其关联的街道、城市、州、国家 $person = Person::where('name', 'John') ->with('street.city.state.country') ->first();
这种写法会执行5次批量查询:
- 查询名为John的Person
- 根据Person的
street_id批量查询对应的Street - 根据Street的
city_id批量查询对应的City - 根据City的
state_id批量查询对应的State - 根据State的
country_id批量查询对应的Country
虽然是多次查询,但都是批量操作,比你原来错误的写法高效很多,而且返回的是完整的Eloquent模型实例,后续可以直接访问关联属性(比如$person->street->city->state->country)或者调用模型方法。
方案二:使用Join(推荐用于只需要少量字段的场景)
如果你的需求仅仅是获取John的基础信息和所属国家的部分字段(比如国家名称),用join可以把所有查询合并为一次,效率更高,但返回的是扁平结果,没有完整的模型关联结构:
// 用Join一次性查询,注意给重复字段加别名避免冲突 $person = Person::select( 'persons.*', 'countries.name as country_name', 'countries.id as country_id' ) ->join('streets', 'streets.id', '=', 'persons.street_id') ->join('cities', 'cities.id', '=', 'streets.city_id') ->join('states', 'states.id', '=', 'cities.state_id') ->join('countries', 'countries.id', '=', 'states.country_id') ->where('persons.name', 'John') ->first();
如果存在Person未关联Street的极端情况,需要把join改成leftJoin,避免数据被过滤掉。
总结
- 若后续需要操作关联模型(比如修改国家信息、调用Country模型的方法),选优化后的预加载;
- 若只需要扁平的字段数据,不需要完整模型,选Join更高效。
内容的提问来源于stack exchange,提问作者bart
相关产品推荐
相关产品推荐

