Laravel多关联查询性能优化求助:仅需10条结果
Laravel多表关联查询优化方案
1. 补充数据库索引
查询慢的核心原因大概率是缺失必要索引,给以下字段添加索引能大幅提升join和where的效率:
- 外键字段:
company_safty_check_reports.company_safty_check_id、company_safty_check_reports.company_safty_check_report_app_id、company_safty_check_report_apps.user_id、company_safty_check_report_vehicles.company_safty_check_report_app_id、company_safty_check_report_vehicles.vehicle_id - 过滤/排序字段:
company_safty_checks.type、company_safty_check_report_apps.company_id、company_safty_check_report_apps.created_at
2. 用Eloquent关联替代原生Join
原生Join容易写出低效查询,用Eloquent模型关联+预加载(with())更易维护,还能避免N+1问题:
先在对应模型定义关联:
// CompanySaftyCheckReport.php public function check() { return $this->belongsTo(CompanySaftyCheck::class, 'company_safty_check_id'); } public function reportApp() { return $this->belongsTo(CompanySaftyCheckReportApp::class, 'company_safty_check_report_app_id'); } // CompanySaftyCheckReportApp.php public function user() { return $this->belongsTo(User::class); } public function vehicles() { return $this->hasMany(CompanySaftyCheckReportVehicle::class); } // CompanySaftyCheckReportVehicle.php public function vehicle() { return $this->belongsTo(Vehicle::class); }
3. 提前过滤核心数据,减少关联范围
原查询是先关联所有表再取10条,应该先筛选出最新的10个company_safty_check_report_apps记录,再基于这些ID关联其他表,大幅减少处理的数据量:
// 先获取符合条件的10个目标APP的ID $targetAppIds = CompanySaftyCheckReportApp::where('company_id', Auth::user()->company_id) ->whereHas('reports.check', function($query) { $query->where('type', 'v'); }) ->orderBy('created_at', 'desc') ->take(10) ->pluck('id'); // 基于筛选后的ID关联查询所需数据,仅返回必要字段 $companySaftyCheckReports = CompanySaftyCheckReport::whereIn('company_safty_check_report_app_id', $targetAppIds) ->with([ 'check:id,title,type', 'reportApp:id,signature,created_at,time_to_complete,time_commenced', 'reportApp.user:id,first_name,last_name', 'reportApp.vehicles:id,vehicle_id', 'reportApp.vehicles.vehicle:id,vehicle_rego' ]) // 只选择当前模型需要的字段,避免select * ->select(['id', 'company_safty_check_id', 'company_safty_check_report_app_id']) ->orderBy('reportApp.created_at', 'desc') ->get();
4. 检查GroupBy的必要性
原查询中groupBy('company_safty_check_report_vehicles.company_safty_check_report_app_id')看起来是为了避免同一个APP返回多条记录(因为一个APP对应多辆车),如果只需要每个APP的一条关联记录,可以替换为distinct()或者在关联时限制单条,比如:
// 在CompanySaftyCheckReportApp模型中新增关联 public function latestVehicle() { return $this->hasOne(CompanySaftyCheckReportVehicle::class)->latest(); }
之后查询时用with('latestVehicle.vehicle')替代vehicles,无需再GroupBy。
5. 分析SQL执行计划
开启查询日志查看生成的SQL,用EXPLAIN分析是否存在全表扫描:
DB::enableQueryLog(); // 执行你的查询 dd(DB::getQueryLog());
把输出的SQL拿到数据库中执行EXPLAIN [你的SQL],查看type列是否为ALL(全表扫描),如果是,对应字段需要补加索引。
内容的提问来源于stack exchange,提问作者Gajanan
相关产品推荐
相关产品推荐

