使用 eloquent 从大型数据集中查询计数

Querying counts from large datasets using eloquent

我有以下关系:

一个工作有很多角色。

public function roles()
{
    return $this->hasMany(Role::class);
}

一个角色有很多轮班和轮班分配。

public function shifts()
{
    return $this->hasMany(Shift::class);
}

public function assignments()
{
    return $this->hasManyThrough(Assignment::class, Shift::class);
}

一个班次有很多作业。

public function assignments()
{
    return $this->hasMany(Assignment::class);
}

我需要统计具有特定状态的所有作业,比方说 "Approved"。这些计数导致我的应用程序运行极其缓慢。以下是我一直以来的做法:

foreach ($job->roles as $role){
    foreach ($role->shifts as $shift) {   
        $pendingCount = $shift->assignments()->whereStatus("Pending")->count();
        $bookedCount = $shift->assignments()->whereIn('status', ["Booked"])->count();
    }
}

我确信一定有更好、更快的方法。其中一些查询需要超过 30 秒以上的时间。有成千上万的作业,我知道这会影响性能。我怎样才能加快这些查询的速度?

您 运行 进入 N+1 问题几次。你想通过作业延迟加载嵌套分配然后你可以访问关系并且你的 where()whereIn() 调用在返回的集合而不是查询构建器上执行,这就是为什么你必须使用 where('status', "Pending") 而不是我示例中的 whereStatus("Pending") 因为集合不会自动解决此约束:

$job = Job::with('roles.assignments')->find($jobId);

foreach ($job->roles as $role) {
    $pendingCount = $role->assignments->where('status', "Pending")->count();
    $bookedCount = $role->assignments->whereIn('status', ["Booked"])->count();
}

这对你来说应该快得多。

更新

您甚至可以更进一步,映射结果并将结果存储在角色的 属性 中:

$job->roles->map(function($role) {
    $role->pending_count = $role->assignemnts->where('status', "Pending")->count();
    $role->booked_count = $role->assignments->whereIn('status', ["Booked"])->count();
    return $role;
});