Laravel-批量插入重复键更新大型数据集


Laravel - Bulk insert on duplicate key update large data set

我有大约 80k 条记录,我需要每天多次运行插入/更新脚本。

INSERT INTO `my_rankings` (`id`, `rank`) VALUES (1,100),(2,99)(3,102)...(80000,3) 
  ON DUPLICATE KEY UPDATE `rank` = values(`rank`);

这些记录当前采用数组格式:

$rankings = [
  ['id' => 1, 'rank' => 100],
  ['id' => 2, 'rank' => 99],
  ['id' => 3, 'rank' => 102],
  ...
  ['id' => 80000, 'rank' => 3],
]

有没有一种很好的/高性能的方式来运行上面提到的更新查询?

我看过雄辩的Model::updateOrCreate(...).但是我认为我不能将其用于批量插入/更新。

我想避免在$rankings数组上使用foreach并插入/更新单个记录,因为脚本将花费太长时间。

我在下面 https://stackoverflow.com/a/34815725/1239122 有一个问题的答案,但它远非优雅。

我有一个解决方案 - 它不优雅,但非常快。 80k 记录为 1.6 秒。任何更好的解决方案将不胜感激。

$allResults = [
    ['id' => 1, 'rank' => 100],
    ['id' => 2, 'rank' => 99],
    ['id' => 3, 'rank' => 102],
    ...
    ['id' => 80000, 'rank' => 3],
];
$rankings = [];
foreach ($allResults as $result) {
    $rankings[] = implode(', ', ['"' . $result['id'] . '"', $result['rank']]);
}
$rankings = Collection::make($rankings);
$rankings->chunk(500)->each(function($ch) {
    $rankingString = '';
    foreach ($ch as $ranking) {
        $rankingString .= '(' . $ranking . '), ';
    }
    $rankingString = rtrim($rankingString, ", ");
    try {
        'DB::insert("INSERT INTO my_rankings (`id`, `rank`) VALUES $rankingString ON DUPLICATE KEY UPDATE `rank`=VALUES(`rank`)");
    } catch ('Exception $e) {
        print_r([$e->getMessage()]);
    }
});

使用数据库事务。

DB::beginTransaction();
foreach ($bajillionRecords as $record) {
    $record->update();
}
DB::commit();

这将存储您的更改,然后在运行 commit() 时将它们提交到数据库。您也可以将其作为闭包:

DB::transaction(function() { /* */ });