2016-01-15 115 views
1

我有大約80k記錄,我需要每天多次運行插入/更新腳本。Laravel - 批量插入重複密鑰更新大型數據集

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], 
] 

是否有一個很好/高性能的方式,我可以運行上述更新查詢?

我看過Eloquent的Model::updateOrCreate(...)。但我不認爲我可以使用這個批量插入/更新。

我想避免在我的$rankings數組上使用foreach並插入/更新單個記錄,因爲腳本將花費太長時間。

我對https://stackoverflow.com/a/34815725/1239122以下的這個問題有個答案,但是它遠非優雅。

回答

2

使用數據庫事務。

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

這將存儲您的更改,然後在您運行commit()時將它們提交到數據庫。你也可以把它作爲一個關閉:

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

感謝這一點,但仍然非常緩慢。花費2分鐘時間處理80k條記錄,即使我將「$ bajillionRecords」分塊。 – Gravy

+0

您是否找到任何解決方案? –

3

我有一個解決方案 - 它不是優雅,但非常快。 1.6萬爲80k記錄。 任何更好的解決方案將不勝感激。

$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()]); 
    } 
});