如何从 eloquent 中的 belongsToMany 关系中获取特定列的最小值

How to get min value of specific column from belongsToMany relationship in eloquent

我在 projects-devices tables 之间有多对多关系。我想 从每个项目的 设备 table 中获取特定列(电池电量) 的最小值。

我只用一个 sql 命令就可以成功。但是我怎样才能用有效的 eloquent 做到这一点呢?谢谢。

第一 table:项目
-- 编号
-- 名字
-- .
-- .
-- .
第二 table:设备
-- 编号
-- battery_level
-- .
-- .
-- .
第三个枢轴table:device_project
-- device_id
-- project_id

表 link 是 here
我想得到的查询结果是 here

原始 sql:

如我所愿,效果很好,但我想用 eloquent 来做。

$projects = DB::select( DB::raw("select
`projects`.`id`, `projects`.`name`,
(select
    `battery_level`
    from `devices`
    inner join `device_project`
    on `devices`.`id` = `device_project`.`device_id`
    where `projects`.`id` = `device_project`.`project_id`
    order by `battery_level`
    limit 1
) as `lowest_battery_level`
from `projects`"));

foreach ($projects as $project) {
    $values[] = $project->lowest_battery_level
}

与eloquent:

这个问题:它会发送两个单独的 sql 查询,尽管我可以通过使用原始 sql 只用 1 个查询来做到这一点。它还从数据库中获取所有设备的电池电量。但是每个项目我只需要最低的一个。

$projects = Project::with('devices:battery_level')->get();

foreach ($projects as $project) {
    $values[] = $project->devices->min('battery_level')
}

在您的项目模型中定义这样的关系

public function devices()
{
return $this->belongToMany(Device::class);
}

并在您的设备模型中定义这样的关系

public function projects()
{
return $this->belongToMany(Project::class);
}

然后在你的控制器中获取项目

$values = [];
$project = Project::find(1); //write your own custom code for gettign the projects
foreach($project->devices as $device)
$values[] = $device->battery_level;

dd($values);

经过多次尝试,我找到了答案。希望这也能帮助到其他人。

addSelect 方法帮助了我,我的 eloquent 代码现在更有效了。这只会创建一个查询,而不会像我想要的那样创建有关设备的详细(不必要的)信息。它仅提供每个项目的最低电池电量。

Eloquent 代码:

$projects = Project::select('projects.id', 'projects.name')
    ->addSelect([
        'lowest_battery_level' => Device::select('battery_level')
        ->leftJoin('device_project', 'device_project.device_id', '=', 'devices.id')
        ->whereColumn('device_project.project_id', 'projects.id')
        ->orderBy('battery_level', 'asc') // no need asc if you wanna get lowest value first
        ->limit(1)
    ])
->get();

// can use like this
foreach ($projects as $project) {
    $values[] = $project->lowest_battery_level
}

这将创建如下 sql 查询:

仅创建 1 个查询并仅获取项目结果而没有其他设备的详细信息。

select
    `projects`.`id`, 
    `projects`.`name`,
    (
        select
            `battery_level`
        from `devices`
        inner join `device_project` on `devices`.`id` = `device_project`.`device_id`
        where `projects`.`id` = `device_project`.`project_id`
        order by `battery_level`
        limit 1
    ) as `lowest_battery_level`
from `projects`

与 Laravel Debugbar

的性能比较

数据库中有 100 个项目1000 个设备。每个项目与 0-50 台设备有随机关系。不同的项目也可以与相同的设备有关系(多对多关系)。

与之前的eloquent代码:

$projects = Project::with('devices:battery_level')->get();

foreach ($projects as $project) {
    $values[] = $project->devices->min('battery_level')
}

As it can be seen below, it uses 18 MB RAM and took 539 ms.
Creates 2783 Device objects and 100 Project objects

使用新的 eloquent 代码:(我在上面显示)

As it can be seen below, it uses 10 MB RAM and took 432 ms.
Creates no Device objects and only 100 Project objects