在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;
}
}