You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 01:11:07