update()适用于全量同值更新,不触发模型事件;upsert()用于存在则更新、不存在则插入,需唯一约束;原生SQL支持行级差异化赋值但兼容性差;依赖模型逻辑或事务安全时应避免批量更新。

用 update() 方法批量更新单字段
当所有记录要更新成同一个值时,update() 最直接。它底层生成的是单条 UPDATE ... WHERE 语句,不是逐条执行,性能没问题。
常见错误是误以为它能一次设不同值——比如给 ID=1 的记录设 status=1,ID=2 的设 status=2,这不行,update() 不支持行级差异化赋值。
- 适用场景:全量开关切换(如
is_active = 0)、统一时间戳(如updated_at = now()) - 写法示例:
User::whereIn('id', [1, 2, 3])->update(['status' => 'archived']) - 注意:该方法跳过模型事件(
saving、updating)和强制类型转换,也不触发 Eloquent 的属性设置逻辑
用 upsert() 实现“存在则更新,不存在则插入”
upsert() 是 Laravel 9+ 提供的真正批量操作接口,底层调用数据库的 INSERT ... ON DUPLICATE KEY UPDATE(MySQL)或 ON CONFLICT DO UPDATE(PostgreSQL)。它能按主键或唯一索引识别记录,并对匹配行更新指定字段。
容易踩的坑是没确认数据库表是否定义了合适的唯一约束——否则 upsert() 会退化为纯插入,甚至报重复键错误。
- 必须提供
$uniqueBy参数,通常是主键(如['id'])或联合唯一索引字段(如['email', 'tenant_id']) - 示例:
User::upsert([['id' => 1, 'name' => 'A'], ['id' => 2, 'name' => 'B']], ['id'], ['name']),表示按id匹配,只更新name字段 - 不支持软删除字段自动处理,若表启用了软删除,需手动在
$uniqueBy中排除deleted_at或先恢复记录
手动拼接批量更新 SQL(绕过 Eloquent)
当需要为每条记录设置不同值,又不想用 N 条单独 UPDATE(性能差),就得手写原生 SQL。Laravel 的 DB::statement() 或 DB::unprepared() 可执行多语句,但更稳妥的是构造一条带 CASE WHEN 的更新语句。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
这种写法数据库兼容性有限:MySQL 和 PostgreSQL 支持 CASE 更新,SQLite 行为可能不一致;且长 SQL 易出错,调试困难。
- 典型结构:
UPDATE users SET status = CASE id WHEN 1 THEN 'active' WHEN 2 THEN 'pending' ELSE status END WHERE id IN (1,2) - 用
DB::select()或DB::update()执行,避免用DB::statement()处理含变量的 SQL,防止注入——务必用参数绑定 - 大数量(如 >500 行)时,分批构造 CASE 块,避免单条 SQL 过长导致 MySQL 报
Packet too large
什么时候不该用批量更新
如果更新逻辑依赖模型访问器(getXXXAttribute)、修改器(setXXXAttribute)、或需触发 saved 事件做后续动作(如发通知、写日志),就别硬上批量。Eloquent 的单条 save() 虽慢,但语义完整。
另一个常被忽略的点是事务安全:批量更新本身不自动包裹事务,而业务上往往要求“全部成功或全部失败”。upsert() 和原生 SQL 都得显式加 DB::transaction(),否则部分失败会导致数据不一致。
复杂字段计算(比如 score = score + 10)也慎用批量——update() 只能设固定值,upsert() 的更新字段不支持表达式,只能靠原生 SQL 的 SET score = score + 10 实现。










