laravel批量更新需按场景选方案:统一字段值用wherein+update();各记录不同值优先upsert();数值增减用increment()/decrement();需模型事件则get()+each()并加事务。

在 Laravel 中做批量更新,不是“选一个方法就行”,而是得看数据要怎么变、表结构支持什么、业务逻辑是否敏感。用错方案轻则性能差、重则丢数据或报错。
统一字段值:用 whereIn + update() 最稳妥
所有目标记录要改成同一个值,比如把一批订单状态全设为“已发货”,就该走这条路:
- ID 数组必须是纯数字,不能是字符串拼接的 "1,2,3"
- 一次别传太多 ID(MySQL 默认超 1000 条可能触发 max_allowed_packet 错误),可手动分批或改用 chunkById
- 它只执行一条 SQL,不触发模型事件,也不走访问器/修改器,纯数据库层操作
每条记录不同值:优先 upsert()(Laravel 9.2+)
你要按 ID 更新各自不同的价格、库存、名称等,且表里有唯一索引(如 id、sku、email),upsert 就是最接近“开箱即用”的方案:
- 第二个参数填匹配字段(如 ['id']),第三个参数填允许更新的字段(如 ['price', 'stock']),别把 id 再塞进第三个参数里
- 不触发 updating/updated 事件,也不调用模型的 setXxxAttribute() 等修改器
- SQLite 用户注意:需 3.24+ 版本,旧版直接报错
数值型字段增减:用 increment() / decrement()
比如给多个商品库存加 5,或给用户积分加 10,千万别先查再改——竞态条件和 N+1 查询会一起爆发:
- increment('stock', 5) 底层生成 SET stock = stock + 5,原子性由数据库保证
- 若还要同时更新 updated_at 或其他字段,得配合 DB::raw():['stock' => DB::raw('stock + 5'), 'updated_at' => now()]
- 不支持多字段不同增量,如 stock +5 同时 price -2,就得换原生 SQL 或事务分步
需要模型事件或业务逻辑:先 get() 再 each() 或 foreach
如果更新前要校验权限、触发日志、调用模型方法、或依赖访问器/修改器,就不能跳过模型实例:
- where(...)->get()->each(fn ($item) => $item->update([...])) 是常见写法,但注意内存占用
- 大数据量时建议用 cursor() 或 lazy() 避免 OOM
- 务必包裹在 DB::transaction() 中,确保全部成功或全部回滚
- 别混淆 update() 返回值:它是整数(影响行数),不是集合,不能 count() 或遍历











