2014-10-01 2 views
6

Я пытаюсь обновить таблицу, содержащую значение slug со случайными слизнями для каждой записи.Laravel Bulk UPDATE

$vouchers = Voucher->get(); // assume 10K for example 

foreach ($vouchers as $voucher) { 
    $q .= "UPDATE vouchers set slug = '" . Str::random(32) . "' WHERE id = " . $voucher->id . ";"; 
} 

DB::statement($q); 

Существует около 2 миллионов записей, поэтому мне нужно выполнить это как объем. Делать это как отдельные записи слишком долго. Кажется, я не могу найти способ их запускать, например, в группах по 10 тыс. Или что-то в этом роде.

Пробовал кучу вариантов ->update() и DB::statement, но, похоже, не может его уйти.

+0

обновление партии (насыпная) Laravel: https://packagist.org/packages/mavinoo/update-batch – Mohammad

ответ

0

Результаты квантования - лучший способ сделать этот материал, не съедая всю вашу оперативную память, а результаты поддержки Laravel - из коробки.

Например:

 
    Voucher::chunk(2000, function($vouchers) 
    { 
    foreach ($vouchers as $voucher) 
    { 
     // 
    } 
}); 
4

Я создал мою пользовательскую функцию для нескольких обновлений, как update_batch в CodeIgniter.

Просто поместите эту функцию в любом из вашей модели или вы можете создать вспомогательный класс и поместить эту функцию в этом классе:

//test data 
/* 
$multipleData = array(
    array(
     'title' => 'My title' , 
     'name' => 'My Name 2' , 
     'date' => 'My date 2' 
    ), 
    array(
     'title' => 'Another title' , 
     'name' => 'Another Name 2' , 
     'date' => 'Another date 2' 
    ) 
) 
*/ 

/* 
* ---------------------------------- 
* update batch 
* ---------------------------------- 
* 
* multiple update in one query 
* 
* tablename(required | string) 
* multipleData (required | array of array) 
*/ 
static function updateBatch($tableName = "", $multipleData = array()){ 

    if($tableName && !empty($multipleData)) { 

     // column or fields to update 
     $updateColumn = array_keys($multipleData[0]); 
     $referenceColumn = $updateColumn[0]; //e.g id 
     unset($updateColumn[0]); 
     $whereIn = ""; 

     $q = "UPDATE ".$tableName." SET "; 
     foreach ($updateColumn as $uColumn) { 
      $q .= $uColumn." = CASE "; 

      foreach($multipleData as $data) { 
       $q .= "WHEN ".$referenceColumn." = ".$data[$referenceColumn]." THEN '".$data[$uColumn]."' "; 
      } 
      $q .= "ELSE ".$uColumn." END, "; 
     } 
     foreach($multipleData as $data) { 
      $whereIn .= "'".$data[$referenceColumn]."', "; 
     } 
     $q = rtrim($q, ", ")." WHERE ".$referenceColumn." IN (". rtrim($whereIn, ', ').")"; 

     // Update 
     return DB::update(DB::raw($q)); 

    } else { 
     return false; 
    } 
} 

Он будет производит:

UPDATE `mytable` SET `name` = CASE 
WHEN `title` = 'My title' THEN 'My Name 2' 
WHEN `title` = 'Another title' THEN 'Another Name 2' 
ELSE `name` END, 
`date` = CASE 
WHEN `title` = 'My title' THEN 'My date 2' 
WHEN `title` = 'Another title' THEN 'Another date 2' 
ELSE `date` END 
WHERE `title` IN ('My title','Another title') 
+0

Мне пришлось изменить эту строку, чтобы она работала, добавив '' окружающие $ data [$ referenceColumn] $ q. = "WHEN". $ ReferenceColumn. "= '". $ Data [$ referenceColumn]. "' THEN '". $ Data [$ uColumn]."' "; После этого он работал, как описано :-) –

+0

Примечание. Я также добавил следующую строку перед последним присваиванием $ q, чтобы удалить последнюю дополнительную запятую из списка areIn. $ whereIn = rtrim ($ whereIn, ','); –

+0

Я обрезал ','. пожалуйста, проверьте правильно.похоже, что вы делаете что-то неправильно –

3

В случае, если кто-то земля в эта страница как я, Laravel позволяет массовое обновление как:

$affectedRows = Voucher::where('id', '=', $voucher->id)->update(array('slug' => Str::random(32)));

См. «Обновление полученной модели» под номером http://laravel.com/docs/4.2/eloquent#insert-update-delete

+1

Это по-прежнему требует от пользователя сделать 2 миллиона вызовов БД, а не массового обновления. – Owen

0

Я сделал функцию массового обновления, которая будет использоваться в моих проектах Laravel. Это намного эффективнее большинства функций, которые я нашел в Интернете. Это может быть полезно для тех, кто хочет использовать пакетный запрос на обновление в Laravel. Его первым параметром является строка имени таблицы, вторая - строка имени ключа, на основе которой вы хотите обновить строку или строки, и большую часть времени это будет «id», а третий параметр - массив данных в следующем формате :

array(
    array(
     'id' => 1, 
     'col_1_name' => 'col_1_val', 
     'col_2_name' => 'col_2_val', 
     //... 
    ), 
    array(
     'id' => 2, 
     'col_1_name' => 'col_1_val', 
     'col_2_name' => 'col_2_val', 
     //... 
    ), 
    //... 
); 

Функция вернет количество затронутых строк. Определение функции:

private function custom_batch_update(string $table_name = '', string $key = '', Array $update_arr = array()) { 

    if(!$table_name || !$key || !$update_arr){ 
     return false; 
    } 

    $update_keys = array_keys($update_arr[0]); 
    $update_keys_count = count($update_keys); 

    for ($i = 0; $i < $update_keys_count; $i++) { 
     $key_name = $update_keys[$i]; 
     if($key === $key_name){ 
      continue; 
     } 
     $when_{$key_name} = $key_name . ' = CASE'; 
    } 

    $length = count($update_arr); 
    $index = 0; 
    $query_str = 'UPDATE ' . $table_name . ' SET '; 
    $when_str = ''; 
    $where_str = ' WHERE ' . $key . ' IN('; 

    while ($index < $length) { 
     $when_str = " WHEN $key = '{$update_arr[$index][$key]}' THEN"; 
     $where_str .= "'{$update_arr[$index][$key]}',"; 
     for ($i = 0; $i < $update_keys_count; $i++) { 
      $key_name = $update_keys[$i]; 
      if($key === $key_name){ 
       continue; 
      } 
      $when_{$key_name} .= $when_str . " '{$update_arr[$index][$key_name]}'"; 
     } 
     $index++; 
    } 

    for ($i = 0; $i < $update_keys_count; $i++) { 
     $key_name = $update_keys[$i]; 
     if($key === $key_name){ 
      continue; 
     } 
     $when_{$key_name} .= ' ELSE ' . $key_name . ' END, '; 
     $query_str .= $when_{$key_name}; 
    } 
    $query_str = rtrim($query_str, ', '); 
    $where_str = rtrim($where_str, ',') . ')'; 
    $query_str .= $where_str; 
    $affected = DB::update($query_str); 

    return $affected; 
} 

Он будет производить и выполнить строку запроса, как это:

UPDATE table_name SET col_1_name = CASE 
WHEN id = '1' THEN 'col_1_value' 
WHEN id = '2' THEN 'col_1_value' 
ELSE col_1_name END, 
col_2_name = CASE 
WHEN id = '1' THEN 'col_2_value' 
WHEN id = '2' THEN 'col_2_value' 
ELSE col_2_name END 
WHERE id IN('1','2')