Laravel中whereIn()为何比关联查询更快?如何优化关联查询?
Laravel关联查询vs whereIn查询性能优化问题解答
首先要明确:你用的两种写法本质上返回的结果集不同——关联查询是获取客户模型集合,同时预加载每个客户对应的托运单;而whereIn写法是直接获取所有符合条件的托运单,不关联客户模型。如果你的需求只是拿到所有托运单,whereIn写法本身就更轻量化,速度快是合理的。但如果需要同时保留客户与托运单的对应关系,关联查询的性能是可以优化的,下面分析可能的问题和解决方案:
关联查询变慢的可能原因
- 不必要的字段查询:关联查询默认会拉取客户表和托运单表的所有字段,如果表中存在大字段(比如text类型、JSON字段),会大幅增加数据传输和内存消耗。
- 关联定义存在额外约束:如果
Customer模型的consignments关联默认添加了orderBy、where等额外条件(比如return $this->hasMany(Consignment::class)->orderBy('created_at')),而这些条件对应的字段没有建立索引,会导致预加载的查询变慢。 - 索引未正确命中:虽然你说加了索引,但需要确认:
- 托运单表的
customer_id字段是否有单独的普通索引; - 如果关联查询有额外过滤条件(比如只查已完成的托运单),是否建立了
customer_id + 过滤字段的复合索引。
- 托运单表的
- 客户数据量过大:如果
where('id','>',1)返回的客户数量极多,即使预加载了托运单,处理大量客户模型集合也会带来额外的内存开销。
关联查询的优化方案
1. 只选择需要的字段
通过select()指定要查询的字段,减少数据传输和内存占用:
// 只查询客户的必要字段,同时指定托运单的必要字段 $customers = Customer::select('id', 'name') ->with(['consignments' => function ($query) { $query->select('id', 'customer_id', 'tracking_no', 'created_at'); }]) ->where('id', '>', 1) ->get();
2. 检查并优化关联定义
如果关联有默认的排序或过滤条件,确保对应字段有索引:
比如关联定义是:
public function consignments() { return $this->hasMany(Consignment::class)->orderBy('created_at', 'desc'); }
那需要给consignments表的created_at字段建立索引,或者建立customer_id + created_at的复合索引,提升排序和查询效率。
3. 对比SQL查询语句
开启Laravel的查询日志,查看关联预加载生成的SQL和手动whereIn的SQL是否有差异,排查是否有额外的低效逻辑:
DB::enableQueryLog(); // 执行关联查询 $customers = Customer::with('consignments')->where('id','>',1)->get(); // 打印查询日志 dd(DB::getQueryLog());
对比日志中的SQL,看预加载的托运单查询是否和手动whereIn的SQL一致,是否有多余的条件导致索引未命中。
4. 替代方案:分组获取托运单
如果你的核心需求是获取客户对应的托运单,不需要完整的客户模型,可以直接查询托运单并按customer_id分组,兼顾性能和对应关系:
$customerIds = Customer::where('id', '>', 1)->pluck('id'); $consignmentsByCustomer = Consignment::whereIn('customer_id', $customerIds) ->get() ->groupBy('customer_id');
这种写法既保留了客户与托运单的对应关系,又避免了处理大量客户模型的开销。
内容的提问来源于stack exchange,提问作者web pakistan
相关产品推荐
相关产品推荐

