首页 > 解决方案 > Laravel 查询优化 (SQL)

问题描述

我的user_level数据库结构是

| user_id | level |
|       3 |     F |
|       4 |    13 |
|      21 |     2 |
|      24 |     2 |
|      33 |     3 |
|      34 |   12+ |

我还有一张桌子users

    |      id | school_id |
    |       3 |         3 |
    |       4 |         4 |
    |      21 |         2 |
    |      24 |         2 |
    |      33 |         3 |
    |      34 |         1 |

我必须实现的是,我必须level根据某个预定义的条件更新每个用户的。但是,我的users表真的很大,有数千条记录。

有一次,我只更新user_level特定学校的记录。说for school_id = 3,我获取所有用户及其关联的级别,然后将这些用户的级别值增加1(F变为1,12+被删除,所有其他数字都增加1)。

当我使用循环遍历用户,匹配他们user_id然后更新记录时,将有数千个查询。这会减慢整个应用程序的速度并导致它崩溃。

一个理想的事情是 laravel 交易,但我怀疑它是否优化了时间。我在一个包含大约 6000 条记录的简单查询中对其进行了测试,它运行良好。但由于某种原因,它与我拥有的记录并没有那么好。

只是看一些关于任何其他查询优化技术的建议。

更新

我实现了一个解决方案,我将根据级别(使用 laravel 集合)对所有记录进行分组,然后与现在的数百/数千个相比,我只需发出 13 个更新查询。

$students = Users::where('school_id', 21)->get();
$groupedStudents = $students->groupBy('level');
foreach ($groupedStudents  as $key => $value) :
        $studentIDs = $value->pluck('id');
        // condition to check and get the new value to update
        // i have used switch cases to identify what the next level should be ($NexLevel)
       UserLevel::whereIn('userId', $studentIDs)->update(["level" => $nextLevel]);
endforeach;

我仍在寻找其他可能的选择。

标签: phpsqllaravel

解决方案


首先在您的模型中定义一个关系,例如:

在 UserLevel 模型中:

public function user() {
      return $this->belongsTo(\App\UserLevel::class);
}

并且您可以仅通过一个查询更新level没有 12+ 级别的查询,并通过一个查询删除所有 12+ 级别

UserLevel::where('level', '<=', 12)->whereHas('user', function($user) {
    $user->where('school_id', 3);
})->update(['level' => DB::raw("IF(level = 'F', 1, level+1)")]);

UserLevel::whereHas('user', function($user) {
    $user->where('school_id', 3);
})->where('level', '>', 12)->delete();

如果您的数据太大。您还可以使用chunk拆分它们以减少内存消耗。

像这样:

UserLevel::where('level', '<=', 12)->whereHas('user', function($user) {
    $user->where('school_id', 3);
})->chunk(5000, function($user_levels) {
    $user_levels->update(['level' => DB::raw("IF(level = 'F', 1, level+1)")]);
});


UserLevel::whereHas('user', function($user) {
    $user->where('school_id', 3);
})->where('level', '>', 12)->delete();

推荐阅读