You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 6中如何批量实现基于id和condition的增改操作?

Laravel 6 批量实现新增/更新(匹配id和condition)

核心限制说明

Laravel 6的Eloquent并没有内置upsert方法(该方法从Laravel 8才开始引入),所以无法直接通过Eloquent的单次调用完成批量的「新增或更新」操作,但可以通过以下两种方式优化你的实现:

方式一:优化现有循环,减少数据库查询次数

你当前的写法存在一个小问题:$id和$condition没有从循环的$data中提取,先修正这个基础写法,再优化查询逻辑:

修正后的基础循环写法

foreach ($data_arr as $data) {
    Model::updateOrCreate(
        ['id' => $data['id'], 'condition' => $data['condition']],
        $data
    );
}

但这种写法每条数据都会触发一次「查询+插入/更新」,数据量大时效率偏低。可以改成先批量查询已存在的记录,再分批次处理插入和更新:

// 1. 提取所有要匹配的id+condition组合
$identifiers = collect($data_arr)->map(function($item) {
    return [$item['id'], $item['condition']];
});

// 2. 批量查询数据库中已存在的记录,用唯一键组合作为数组键
$existingKeys = Model::where(function($query) use ($identifiers) {
    foreach ($identifiers as [$id, $condition]) {
        $query->orWhere(function($q) use ($id, $condition) {
            $q->where('id', $id)->where('condition', $condition);
        });
    }
})->get()->mapWithKeys(function($item) {
    return [$item->id . '|' . $item->condition => $item];
});

// 3. 拆分出需要插入和更新的数据
$insertData = [];
$updateItems = [];

foreach ($data_arr as $data) {
    $key = $data['id'] . '|' . $data['condition'];
    if (isset($existingKeys[$key])) {
        $updateItems[] = array_merge(
            ['id' => $data['id'], 'condition' => $data['condition']],
            $data
        );
    } else {
        $insertData[] = $data;
    }
}

// 4. 批量插入新数据
if (!empty($insertData)) {
    Model::insert($insertData);
}

// 5. 批量更新已有数据(用CASE WHEN实现高效批量更新)
if (!empty($updateItems)) {
    $casesCol1 = collect($updateItems)->map(function($item) {
        return "WHEN id = ? AND condition = ? THEN ?";
    })->implode(' ');
    $casesCol2 = collect($updateItems)->map(function($item) {
        return "WHEN id = ? AND condition = ? THEN ?";
    })->implode(' ');

    // 整理绑定参数
    $bindings = [];
    foreach ($updateItems as $item) {
        $bindings[] = $item['id'];
        $bindings[] = $item['condition'];
        $bindings[] = $item['col1'];
    }
    foreach ($updateItems as $item) {
        $bindings[] = $item['id'];
        $bindings[] = $item['condition'];
        $bindings[] = $item['col2'];
    }
    foreach ($updateItems as $item) {
        $bindings[] = $item['id'];
        $bindings[] = $item['condition'];
    }

    DB::update("
        UPDATE your_table 
        SET col1 = CASE $casesCol1 ELSE col1 END,
            col2 = CASE $casesCol2 ELSE col2 END
        WHERE (id, condition) IN (" . collect($updateItems)->map(fn() => '(?, ?)')->implode(',') . ")
    ", $bindings);
}

这种方式将原来的N次查询减少为1次查询,再通过批量插入和批量更新完成操作,效率比原始循环高很多。

方式二:用原生SQL的INSERT ... ON DUPLICATE KEY UPDATE(推荐)

这是效率最高的方式,需要先给表添加id和condition的联合唯一索引,让MySQL能识别冲突行:

1. 添加联合唯一索引

ALTER TABLE your_table ADD UNIQUE INDEX idx_id_condition (id, condition);

2. 构造批量SQL语句

用DB门面执行原生SQL,通过参数绑定避免注入问题:

// 定义要操作的字段列
$columns = ['id', 'condition', 'col1', 'col2'];

// 准备绑定参数和占位符
$bindings = [];
$placeholders = collect($data_arr)->map(function($item) use (&$bindings, $columns) {
    $rowPlaceholders = [];
    foreach ($columns as $col) {
        $rowPlaceholders[] = '?';
        $bindings[] = $item[$col];
    }
    return '(' . implode(',', $rowPlaceholders) . ')';
})->implode(',');

// 构造更新子句:冲突时更新指定字段
$updateClause = collect($columns)
    ->filter(fn($col) => !in_array($col, ['id', 'condition']))
    ->map(fn($col) => "$col = VALUES($col)")
    ->implode(',');

// 执行批量插入/更新
DB::statement(
    "INSERT INTO your_table (" . implode(',', $columns) . ") 
     VALUES $placeholders 
     ON DUPLICATE KEY UPDATE $updateClause",
    $bindings
);

这种方式只需要一次数据库请求就能完成所有数据的新增或更新,效率最优。


内容的提问来源于stack exchange,提问作者pileup

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 07:15:41