laravel 批量更新:‌INSERT ... ON DUPLICATE KEY UPDATE

在SQL批量更新时可通过INSERT ... ON DUPLICATE KEY UPDATE 语句进行批量更新,具体做法是,在插入数据时处理唯一索引或主键冲突,不执行插入操作,而是执行指定的更新操作。

INSERT INTO table_name(column1, column2, ...) VALUES(value1, value2, ...) ON DUPLICATE KEY UPDATE column1=value1, column2=value2, ...

例如我们可以通过传主键id来进行插入数据冲突,从而实现批量更新数据,具体用laravel实现的话,我们可以用原生语句 + 绑定参数来进行实现,代码具体实现为

    /**
     * @param Model|mixed $model 需要更新数据模型类 如: new App\Models\User ()
     * @param array $attributesList 更新数据内容,如:[['id' => 1, 'name' => "小王"], ['id' => 2, 'name' => "小刘"]]
     * @param array $updateKey 需要更新的字段,必须是表中包含的字段 ['id', 'name']
     * @param string $id 主键名称,默认为 id
     * @param int $size 批量分批更新数量 默认100条数据
     * @return void
     * @throws \Exception
     */
    public static function batchUpdate($model, array $attributesList, array $updateKey, string $id = 'id', int $size = 100): void
    {
        //更新内容空直接返回
        if (!$attributesList) {
            return;
        }
        // 判断id是否为主键
        if ($id !== $model->getKeyName()) {
            throw new \RuntimeException($id . '不为主键不能进行批量更新');
        }
        // 主键需存在 更新字段中
        if (!in_array($id, $updateKey, false)) {
            throw new RuntimeException('主键必须存在更新的字段中');
        }
        
        // 更新字段在模型表中
        $columns = Schema::getColumnListing($model->getTable());
        if ($columns && is_array($columns)) {
            foreach ($updateKey as $updateItem) {
                if (!in_array($updateItem, $columns, false)) {
                    throw new RuntimeException($updateItem . '不存在表中,无法进行更新');
                }
            }
        }
        
        //更新字段要存在与更新内容中
        $primaryIds = [];
        foreach ($attributesList as $item) {
            if ($item[$id]) {
                $primaryIds[] = $item[$id];
            } else {
                throw new RuntimeException('存在' . $id, '为空');
            }
            foreach ($updateKey as $column) {
                if (!isset($item[$column])) {
                    throw new RuntimeException("批量更新失败!传入的数据中不存在字段{$column}!", 500);
                }
            }
        }
        //更新的内容必须存在于表中
        if (count($primaryIds) !== $model::query()->whereIn($id, $primaryIds)->count()) {
            throw new RuntimeException('存在不在记录中的数据');
        }

        
        DB::beginTransaction();
        try {
            //分批进行更新
            //更新语句 如:INSERT INTO user(`id`,`name`) values(:id_0,:name_0),(:id_1,:name_1) ON DUPLICATE KEY UPDATE `id`=values(`id`),`name`=values(`name`)
            //绑定参数 如 ['id_0' => 1, 'name_0' => '小王', 'id_1' => 1, 'name_1' => '小刘']
            foreach (array_chunk(array_values($attributesList), $size) as $dataChunkList) {
                $dataChunkList = array_values($dataChunkList);
                $sql = 'INSERT';
                $sql .= ' INTO ' . $model->getTable() . '(`' . implode('`,`', $updateKey) . '`) values';
                foreach ($dataChunkList as $key => $data) {
                    $sql .= '(';
                    foreach ($updateKey as $column) {
                        $sql .= ":{$column}_{$key},";
                    }
                    $sql = substr($sql, 0, -1) . '),';
                }
                $sql = substr($sql, 0, -1);
                $sql .= ' ON DUPLICATE KEY UPDATE ';
                foreach ($updateKey as $column) {
                    $sql .= "`{$column}`=values(`{$column}`),";
                }
                $sql = substr($sql, 0, -1);

                $bindParam = [];
                foreach ($dataChunkList as $key => $data) {
                    foreach ($updateKey as $column) {
                        //更新字段要存在与更新内容中
                        $bindParam["{$column}_{$key}"] = $data[$column];
                    }
                }
                $bool = DB::insert($sql, $bindParam);
                if (!$bool) {
                    throw new RuntimeException("更新异常");
                }
            }
            DB::commit();
        } catch (\Exception $exception) {
            DB::rollBack();
            throw $exception;
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值