我在Laravel App中有以下代码:我读取.csv文件中的每一行,并希望更新值。但是,倍数的更新查询使用8k行的.csv时非常慢。我如何加快此代码的速度?谢谢
DB::beginTransaction();
try {
$delimiter = ",";
$firstLine = true;
if ($handle !== FALSE) {
$position = 1;
while (($csv_line = fgetcsv($handle, 1000, $delimiter)) !== FALSE) {
if ($firstLine == true) {
$firstLine = false;
continue;
}
$player_uid = $csv_line[0];
DB::table('scores')
->where('season_uid', $season_uid)
->where('day', $day)
->where('player_uid', $player_uid)
->update(['position' => $position]);
$position++;
}
fclose($handle);
}
DB::commit();
return true;
} catch (\Exception $e) {
Log::error($e);
DB::rollBack();
return false;
}
答案 0 :(得分:0)
MySQL不支持批量更新,但是有一个巧妙的技巧可以使用ON DUPLICATE KEY UPDATE子句用插入替换更新。这样,您实际上可以批量更新记录。看看这个答案for some examples。
据我所知,尽管Laravel在其查询生成器中不支持此子句,所以您将必须手动生成查询并通过 DB::statement()
发出查询。确保对传入的行进行分块(例如,减少100条),您将看到速度显着提高。
但是要意识到,更新8k行并不是一个便宜的操作。最佳实践是将其委派给单独的作业,并在应用程序中设置队列,以便工作人员可以在后台分别处理这些更新。您可以了解有关作业和队列in the official documentation的更多信息。
答案 1 :(得分:0)
确实建议使用单独的作业来执行此操作,但是您可以尝试以下代码。在https://github.com/laravel/ideas/issues/575上找到了创建单个更新查询的想法。这个家伙减少了加载时间,最终快了13倍。
请注意,之前尚未对其进行测试。
DB::beginTransaction();
try {
$csv = array_map('str_getcsv', file('data.csv'));
// remove the first line
array_shift($csv);
// grab only the players uids and their positions
$positions = array_flip(array_column($csv, 0));
array_walk($positions, static function(&$position, $id) {
$position = "WHEN {$id} THEN {$position}";
});
DB::update("UPDATE `scores`
SET `position` = CASE `player_uid` " . implode(' ', $positions) . " END
WHERE `player_uid` in (" . implode(',', array_keys($positions)) . ")
AND `session_uid` = ?
AND `day` = ?", [$season_uid, $day]);
DB::commit();
return true;
} catch (\Exception $e) {
Log::error($e);
DB::rollBack();
return false;
}
PS:最好用这种方法写一篇有关性能变化的评论