【问题标题】:Update in bulk - Laravel批量更新 - Laravel
【发布时间】:2020-10-02 12:30:43
【问题描述】:

我正在尝试寻找一种方法来一次更新多条记录,就像我们使用 insert 一次创建多条记录一样。

假设我有一个关联数组,其中包含要在员工表的每一行中更新的数据:

$data = [
  ['id'=>1,'name'=>'xxx'],
  ['id'=>2,'name'=>'xxx'],
  ['id'=>3,'name'=>'xxx'],
  ['id'=>4,'name'=>'xxx']
];

更新所有这些记录的一种方法是:

foreach($data as $d){
   Employee::where('id'=>$d['id'])->update(['name'=>$d['name']]);
}

其中 Employee 是员工表的模型。

我想知道我是否可以通过一行语句更新所有记录?

例如,如果我从 $data 创建新记录,我会使用:

Employee::insert($data);

而不是遍历 $data 并为每条记录使用 create()。

还有什么类似的东西可以更新吗?

【问题讨论】:

  • @Marcin Gierus,请在放置其他问题的链接之前更好地理解问题。我不是要求插入新行,我已经详细解释过了。我无法进一步解释
  • 请你推理一下需求
  • @maytham-ɯɐɥʇʎɐɯ, 要求是优化代码并以干净的方式实现。
  • 感谢大家的贡献..我通过接受 Laravel 中的批量更新是不可能的来关闭这个线程。

标签: laravel laravel-5


【解决方案1】:

这是不可能的,因为只能通过union 进行批量更新,这是您可以在 sql 中使用的最慢的运算符。 否则,即使在原始 sql 中,也无法使用不同行中的不同值批量更新,因此在 laravel 中也没有办法。

【讨论】:

    【解决方案2】:

    不,这是不可能的,你应该遍历数组并分别更新每一行。

    【讨论】:

    • @Learner 你可以创建自己的助手来处理这个问题并将数据传递给它,比如->updateMultiple($data),但是这个代码仍然会为每个更新操作生成单独的数据库查询。
    • @ Alexey Mezenin,是的,你是对的,只是将代码分发到单独的文件中并不是我想要的。但是感谢您的宝贵时间。
    【解决方案3】:

    你试过Employee::update($data);吗?当然数据应该是数组 喜欢: $data = (['name'=>'test'],['surname'=>'test2'])); - 它将更新表中的所有行 - 但您可以添加 where 条件。

    【讨论】:

    • :))) 好的,我在这里再次尝试解释。我不知道你为什么这么不清楚。我想一次更新多行。您建议的方式是一次更新一行。我已经提出了一个有问题的解决方案,一次更新一行,但这不是我想要的。我想一次更新多行。与insert() 相同,一次创建多个行。我很好奇是否存在一些本机 laravel 函数来做同样的事情,但用于更新而不是创建。换句话说,你提议的是create()equivalent 用于创建新行,我想要insert()equivalen
    • 非常感谢您的努力。但正如您在我的问题中可能注意到的那样,我需要更新具有不同 id 而不是相同 id 的多行。另外,我的每一行都有不同的name 字段,因此您的链接在我的情况下不起作用,因为:1. 他们有相同的 id,他们想更改行:item_type_id,2. 他们有相同的值,他们想更新行:'颜色'=>'黑色'。希望你能理解。
    • :P 我不会放手的,如果你有不同的 id 你可以使用 laravel whereIn 更新,例如:$users = User::whereIn('id', array(1, 2, 3))->update($data); 不知道 whereIn 是否可以与两个参数一起使用:D
    • 也许这就是 Alexey Mezenin 所说的,根本不可能。但我很高兴你遇到了问题并且仍在尝试。干杯。
    【解决方案4】:

    Doctrine 允许您进行批量插入/更新。你可以在这里找到它:http://www.laraveldoctrine.org/

    【讨论】:

    • 谢谢,但我不想安装完整的扩展只是为了避免 foreach,因为无论如何我都有 insert() 插入多个数据。我希望也许有人知道一些 laravel 本机功能或更有效的方法。我最好使用一个 foreach 而不是添加额外的扩展。
    【解决方案5】:

    使用集合来做这样的事情怎么样?

    $data = [
      ['id'=>3,'name'=>'xxx'],
      ['id'=>4,'name'=>'xxx'],
      ['id'=>5,'name'=>'xxx']
    ];
    
    Employee::find(collect($data)->pluck('id')->toArray())->map(function($item, $key) use ($data){
      $item['name'] = $data[$key]['name'];
      return $item->save();
    });
    

    【讨论】:

    • 啊,我刚刚意识到这只有在事先对 id 进行排序的情况下才有效。
    • 这是一个很好的解决方案。虽然问题是如果你分析你的构造,它会比一个简单的 foreach 语句花费更多的时间。它并不比 foreach 方法更有效。
    • 同意,不过这是一种替代方案。我认为没有比 foreach 更简单的解决方案,因为如果可以使用纯 SQL 来完成,Eloquent 就可以。
    • 好吧,事实上,在原始查询stackoverflow.com/questions/20255138/… 中是可能的,但不幸的是在 laravel 的 eloquent 中没有实现。
    • 啊,我明白了,有道理。然后您可以动态构建查询,但这可能不会比 foreach 更简单。
    【解决方案6】:

    我写了一个批量更新函数用于我的 Laravel 项目。对于想要在 laravel 中使用批量更新查询的人来说,它可能很有用。它的第一个参数 是表名字符串,第二个是要更新行的键名字符串,主要是“id”,第三个参数是数据数组,格式如下:

    array(
        array(
            'id' => 1,
            'col_1_name' => 'col_1_value',
            'col_2_name' => 'col_2_value',
            //....
        ),
        array(
            'id' => 2,
            'col_1_name' => 'col_1_value',
            'col_2_name' => 'col_2_value',
            //....
        ),
        //....
    );
    

    函数定义:

    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')
    

    【讨论】:

      【解决方案7】:

      它将在 laravel 中工作以更新现有或新插入到表中。

      $data=array(  
          array('id'=>1,'name'=>'kaleemullah@example.com'),
          array('id'=>2,'name'=>'kaleemullah@example.com2'),
          array('id'=>4,'name'=>'kaleemullah@example.com4')
      );
      $imported =  implode(', ', array_map(function ($string) {
      return "(".$string['id'].','."'".$string['name']."'"."),";
      }, $data));
      $OneStepAway =  str_replace(',,', ',', $imported);
      $kale = rtrim($OneStepAway,',');
      $s = DB::statement("INSERT INTO  tests (id,name) VALUES $kale
      ON DUPLICATE KEY UPDATE name=VALUES(name)") ;
      echo "Successfully Updated and Insert New records into the database ";
      

      【讨论】:

        【解决方案8】:

        对于一小块数据,循环就可以了。

        但是,如果您有大量数据 - 绝对不要使用循环,因为 “数据库事务”是一项昂贵的操作,并且使用 for 循环,您将结束为每次迭代做一个事务。

        在这里您可以看到如何在没有 for 循环的情况下进行更新:

        $data = [
          ['id'=>1,'name'=>'xxx'],
          ['id'=>2,'name'=>'xxx'],
          ['id'=>3,'name'=>'xxx'],
          ['id'=>4,'name'=>'xxx']
        ];
        
        $cases = [];
        $ids = [];
        $params = [];
        
        foreach ($data as $datum) {
           $id = $datum['id'];
        
           $cases[] = "WHEN {$id} then ?";
           $params[] = $datum['name'];
           $ids[] = $id;
        }
        
        $ids = implode(',', $ids);
        $cases = implode(' ', $cases);
        
        if (!empty($ids)) {
            \DB::update("UPDATE employees SET `name` = CASE `id` {$cases} END WHERE `id` in ({$ids})", $params);
        }
        

        如果您想了解它,请查看https://medium.com/@sentiasa/bulk-update-multiple-records-with-separate-data-laravel-3da9131c279a

        【讨论】:

          【解决方案9】:
          public function transaction($callback) {
              DB::connection(**<TABLE_NAME>**)->transaction($callback);
          }
          
          public function updateBatch($batchData)
          {
              $count = 0;
              try {
                  $batchData = array_values($batchData);
                  $total = count($batchData);
                  $j = 0;
                  $i = 0;
                  do {
                      $j = min($total -1, $j + self::$configBatch);
                      DB::connection(**<TABLE_NAME>**)->transaction(function() use ($batchData, &$count, $total, $j, &$i) {
                          for (; $i <= $j; $i++) {
                              $batchData[$i]->save();
                              $count++;
                          }
                      });
                  } while ($i < $total);
              } catch (\Exception $exception) {
                  print_r($exception->getMessage());
                  $count = 0;
              }
              return $count;
          }
          

          希望能帮到你

          【讨论】:

            猜你喜欢
            • 2014-11-25
            • 1970-01-01
            • 2014-07-15
            • 2019-01-02
            • 1970-01-01
            • 2021-06-28
            • 1970-01-01
            • 1970-01-01
            • 2013-06-09
            相关资源
            最近更新 更多