saveAll()用于逐条判断新增或更新,需严格匹配主键字段名并触发模型事件;update()是原生SQL批量更新,性能高但绕过模型逻辑、不触发钩子,且必须带WHERE条件防全表误改。
和
是 ThinkPHP 批量更新的两个主力方法,但它们适用场景完全不同。用错一个,轻则字段乱写、钩子不触发,重则全表清空、锁表超时。
用
更新已有记录,主键必须对得上
它本质是「逐条判断新增 or 更新」:有主键值 → 走 UPDATE;没主键或主键不存在 → 走 INSERT。常见失效原因不是代码写错,而是主键字段名不一致。
查模型类里的
,前端传的必须是
,传
就会当成新记录插入
数据里不能漏主键 ——
中第二条因无
,直接变成 INSERT
想触发
等模型事件,
是唯一能走通的批量方式;
静态调用不走任何模型钩子
开启严格模式可提前暴露问题:
,缺失主键时直接报错,不静默插入
用
或
批量改状态
这是真正下推到 SQL 的批量更新,性能高、不查库,但完全绕过模型逻辑。适合「把所有 status=0 的订单改成 1」这类简单条件更新。
是正确写法;错写成
会无效(实例方法只作用于当前对象)
更快,但不触发事件、不处理时间戳、不校验字段合法性
务必带
——
会清空全表
,ThinkPHP 6+ 默认不拦截空条件
开发期建议加全局 SQL 监听,拦截无 WHERE 的 UPDATE:
只想更新几个字段,别让其他字段被覆盖
ThinkPHP 默认会把模型所有非空属性塞进 SET 子句,哪怕你只改了
,
、
也可能被写进去。
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
用
显式限定:
时间戳字段默认强制写入,要关掉得用开关参数:
,注意这不是传值,是传布尔开关
字段含点号(如
)不支持
,只能切到原生
想做
这类计算更新,必须用
或
大批量 ID 更新卡死?先看是不是数据库锁住了
不是 PHP 慢,是 MySQL 在执行
时,遇到 ID 不连续或数量过大(比如上万),可能升级为表锁或触发间隙锁,阻塞其他请求。
立即学习
“
PHP免费学习笔记(深入)
”;
别拼字符串 IN 条件:
有 SQL 注入风险;必须用
确保
字段有索引,否则全表扫描 + 锁表,比慢还危险
拆分批次处理:
分批,每批独立事务,避免单次锁太久
如果条件是时间范围(如
),更要确认该字段已建索引
实际项目里最常踩的坑,往往不在语法对错,而在「以为自己在用模型逻辑,其实已经切到了原生 SQL」——比如调了
还在等
触发,或者传了
却忘了模型主键是
。这些地方没有运行时报错,只有数据行为异常,排查起来反而最耗时间。
saveAll()update()saveAll()$pk = 'id'['id' => 123]['user_id' => 123][['id'=>1,'status'=>1], ['status'=>2]]idbefore_updatesaveAll()update()Db::setConfig(['strict' => true])Model::update()Db::table()->update()Order::update(['status' => 1], ['status' => 0])$order->where(...)->update(...)Db::table('order')->whereIn('id', $ids)->update(['status' => 1])whereDb::table('user')->update(['status'=>0])statusDb::listen(function ($sql) { if (stripos($sql, 'UPDATE') !== false && !preg_match('/WHERE/i', $sql)) { throw new Exception('Missing WHERE in UPDATE'); } });statusnamecreated_atallowField()$user->allowField(['status', 'updated_at'])->save(['status' => 2])save(['status' => 2], ['update_time' => false])profile.ageallowField()Db::table()->where()->update()score = score + 10['score' => ['exp', 'score + 10']]Db::raw('score + 10')UPDATE ... WHERE id IN (1,2,3,...)where('id', 'in', implode(',', $ids))whereIn('id', $ids)idarray_chunk($ids, 500)created_time < '2025-01-01'Model::update()afterUpdateuser_idid