Laravel如何减少数据查询次数 解决循环导致的N+1查询问题
Laravel 循环统计N+1查询优化方案
你当前的问题是典型的循环内查库导致的N+1性能问题:100行数据每行单独跑聚合SQL,再加另一个同类统计方法,最终产生200条左右的冗余查询,数据量上涨后页面加载速度会明显变慢。
核心优化思路是:所有数据库查询全部前移到控制器/服务层完成,禁止在Blade模板循环内调用带查询逻辑的方法,通过单次批量聚合拿到所有需要的统计值,做键值映射后直接传给模板渲染。
具体实现步骤
1. 控制器层批量预查询统计值
只需要1条SQL就能拿到所有需要的卡车数统计结果,替换原来循环内100次单独查询:
// 保留原有日期筛选逻辑 $dateForFilter = $isToday ? Carbon::today() : getTommorrowDate(); // 从已有deals集合里提取所有去重的调度方ID,避免重复统计相同ID $assignerIds = $deals->pluck('truck_assigner_id')->unique()->filter()->toArray(); // 按调度方ID分组聚合,单次查询拿到所有ID对应的卡车总和 $truckCountMap = Deal::where('packaging_company_id', $user->owner_id) ->whereIn('truck_assigner_id', $assignerIds) ->whereDate('date_of_work', $dateForFilter) ->groupBy('truck_assigner_id') ->pluck(\DB::raw('SUM(no_of_trucks) as total_trucks'), 'truck_assigner_id') ->toArray(); // 另一个逻辑类似的统计方法也用同样的思路处理,最终总共只需要2条SQL // 第二个统计实现逻辑和上面完全一致:提取关联ID、批量分组查询、生成键值映射 return view('对应模板文件', compact('deals', 'truckCountMap', 'xxxSecondCountMap'));
2. 修改Blade模板渲染逻辑
模板里不再调用带数据库查询的方法,直接从预先生成的映射数组里取值,渲染阶段零查询:
@foreach($deals as $deal) <tr> <td> @php $truckCount = $truckCountMap[$deal->truck_assigner_id] ?? 0; @endphp {{ $truckCount > 0 ? "({$truckCount} ocp) " : '' }} {{-- 第二个统计值同理直接从映射数组取即可 --}} </td> </tr> @endforeach
额外优化建议
- 可以给
deal表的packaging_company_id、truck_assigner_id、date_of_work三个字段建立联合索引,聚合查询的速度会有明显提升。 - 所有数据查询、计算逻辑统一收敛到控制器、模型层或者Repository层,Blade模板只负责输出处理好的变量,从根源上避免模板里写查库逻辑的问题。
- 如果后续多个页面用到同类统计,可以把批量聚合的逻辑封装成公共方法,传入ID集合、筛选条件直接返回映射数组,减少重复代码。
内容的提问来源于stack exchange,提问作者web pakistan
相关产品推荐
相关产品推荐

