批量操作应优先用Query Builder的insert()、whereIn()->update()、whereIn()->delete()和upsert(),避免循环create()或无where的update(),注意字段映射、唯一约束、软删除及错误处理。

批量插入用 insert(),别用 create() 循环
单条 create() 走模型事件和验证,循环插入 100 条就是 100 次查询 + 100 次事件触发,慢且容易超时。真要批量写入,绕过 Eloquent,直走 Query Builder 的 insert()。
- 数据必须是二维数组,键名需与数据库字段完全一致(
['name' => 'a', 'email' => 'a@b.com']),不能带模型属性如created_at(除非显式设置) - 不触发
creating/created等模型事件,也不走fillable或casts,时间字段得自己now() - MySQL 有
max_allowed_packet限制,单次插入别超 1000 行;拆分用collect($data)->chunk(500) - 示例:
DB::table('users')->insert($users);
批量更新慎用 update(),条件不匹配就静默失败
update() 是“全表扫+条件改”,没加 where 就是全表更新,线上库出过不止一次事故。更麻烦的是:它不返回“影响行数为 0”这种提示,前端以为成功了,其实一条没改。
- 必须显式传
where条件,推荐用主键数组:User::whereIn('id', $ids)->update(['status' => 1]); - 想确认是否真改了,得手动查:
User::whereIn('id', $ids)->count()对比 $ids 数量 - 如果要按不同 ID 设不同值(比如批量调价),
update()不支持,得用upsert()(Laravel 9+)或原生 SQLCASE WHEN - 软删除模型慎用:默认忽略
deleted_at IS NULL条件,要加withTrashed()才能更新已删记录
批量删除别直接 delete(),先确认再动手
delete() 在集合上调用会触发每条的模型事件;在查询构造器上调用则跳过事件但不可逆。最危险的是——它不校验权限、不记日志、不抛异常,删错没法回滚。
- 永远优先用
whereIn('id', $ids)->delete(),而不是collect($models)->each->delete() - 软删除下,
delete()只是设deleted_at,真删要用forceDelete(),但这个方法不支持批量调用,得拆成循环(注意 N+1) - 上线前加防护:检查 $ids 长度,超 1000 报警;关键表(如 orders)加白名单 ID 前缀校验
- 删除后建议补一句日志:
Log::info('Batch delete users', ['ids' => $ids, 'by' => auth()->id()]);
Laravel 9+ 用 upsert() 替代“查再插”逻辑
老办法是先 firstOrCreate() 循环,性能差还容易死锁。Laravel 9 引入 upsert(),底层走 INSERT ... ON DUPLICATE KEY UPDATE,单条语句完成存在即更新、不存在即插入。
- 必须指定唯一索引字段(如
email或联合索引),否则报错SQLSTATE[HY000]: General error: 1205 Deadlock found - 语法:
User::upsert($data, ['email'], ['name', 'updated_at']);
第二个参数是“判断唯一性的列”,第三个是“冲突时更新的列” - MySQL 要求对应字段有 UNIQUE 约束,PostgreSQL 要
ON CONFLICT子句支持,SQLite 不支持 - 不会触发
saving等事件,updated_at也得手动塞进更新字段里,不然不会自动刷新










