基于外键使用Eloquent批量插入更新多行的最优方案
最优实现方案:批量UPSERT(插入或更新)
首先必须先做一个关键准备:给你的answers表添加唯一组合索引,因为我们需要靠customer_id和question_id的组合来判断记录是否存在。你可以通过迁移文件或者直接执行SQL来创建:
// 迁移文件示例 public function up() { Schema::table('answers', function (Blueprint $table) { $table->unique(['customer_id', 'question_id']); }); }
或者直接执行SQL:
ALTER TABLE answers ADD UNIQUE INDEX idx_customer_question (customer_id, question_id);
方案1:Laravel 8+ 原生支持的 upsert 方法(最推荐)
从Laravel 8开始,Eloquent原生提供了upsert方法,完美适配你的需求——一条SQL完成批量的“存在则更新,不存在则插入”,完全不需要循环处理,性能拉满。
具体实现代码如下:
use App\Models\Answer; // 解析请求数据 $requestData = request()->validate([ 'customer_id' => 'required|integer', 'answer' => 'required|array', ]); $customerId = $requestData['customer_id']; $answers = $requestData['answer']; // 转换为upsert需要的格式:每个元素是包含三个字段的数组 $upsertData = collect($answers)->map(function ($value, $questionId) use ($customerId) { return [ 'customer_id' => $customerId, 'question_id' => $questionId, 'value' => $value, ]; })->toArray(); // 执行批量UPSERT Answer::upsert( $upsertData, ['customer_id', 'question_id'], // 用来判断重复的唯一键组合 ['value'] // 当记录存在时,需要更新的字段 );
这个方法的底层就是利用MySQL的INSERT ... ON DUPLICATE KEY UPDATE语法,所有操作在单条SQL中完成,不管你有多少条答案,性能都不会有问题。
方案2:兼容Laravel 7及以下版本(原生SQL实现)
如果你的Laravel版本低于8.x,无法使用upsert方法,可以直接通过DB门面构造原生的批量UPSERT语句:
use Illuminate\Support\Facades\DB; // 同样先解析请求数据,得到$customerId和$answers数组 // 构造插入字段和值 $columns = ['customer_id', 'question_id', 'value']; $bindings = []; $valuePlaceholders = []; foreach ($answers as $questionId => $value) { $valuePlaceholders[] = '(?, ?, ?)'; $bindings[] = $customerId; $bindings[] = $questionId; $bindings[] = $value; } // 构造SQL语句 $sql = sprintf( "INSERT INTO answers (%s) VALUES %s ON DUPLICATE KEY UPDATE value = VALUES(value)", implode(', ', $columns), implode(', ', $valuePlaceholders) ); // 执行SQL(参数绑定更安全,避免SQL注入) DB::statement($sql, $bindings);
第三方库方案(非必需)
其实上面的官方方案已经完全满足需求了,但如果你需要更复杂的批量操作逻辑(比如更新多个字段、自定义更新规则),可以考虑使用第三方包,比如yadakhov/insert-on-duplicate-key。不过个人建议优先用官方原生方法,避免引入额外依赖。
举个该包的使用示例(先通过Composer安装):
composer require yadakhov/insert-on-duplicate-key
然后使用:
use Yadakhov\InsertOnDuplicateKey; $insert = new InsertOnDuplicateKey(); $insert->table('answers') ->columns(['customer_id', 'question_id', 'value']) ->values($upsertData) ->onDuplicateKeyUpdate(['value' => 'VALUES(value)']) ->execute();
内容的提问来源于stack exchange,提问作者Michael Chandra Lrc
相关产品推荐
相关产品推荐

