更新已有记录时优先用 update() 而非 save();save() 触发完整生命周期但易误插重复数据,update() 更轻量可控;批量更新必须用 updateall() 避免 n 次 sql。

update() 和 save() 到底该用哪个
更新已有记录时,save() 是最常用但最容易误用的方法;它会先触发验证、再判断是否为新记录,最后决定执行 INSERT 还是 UPDATE。如果你已经明确知道这是已有数据(比如通过 findOne() 查出来的),直接调用 update() 更轻量、更可控。
常见错误现象:$model->name = 'new'; $model->save(); 在字段被设为 safe 但未在 rules() 中声明时静默失败;或因时间戳行为(TimestampBehavior)意外覆盖 updated_at 值。
-
save():适合需要完整生命周期(验证 → 事件 → 保存)的场景,但必须确保getIsNewRecord()返回false,否则可能插入重复主键 -
update():跳过验证和新增逻辑,只生成 UPDATE 语句;可传入$runValidation = false和$attributeNames明确指定更新字段,避免脏数据污染 - 若模型启用了
TimestampBehavior,update()默认仍会更新updated_at;如需禁用,得在行为配置中设'attributes' => [ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at']]并手动清除
批量更新别用 foreach + save()
用 foreach 遍历模型数组再逐个 save() 或 update(),本质是 N 次独立 SQL 执行,性能极差,且事务控制困难。100 条记录就是 100 次 UPDATE,还可能因中间失败导致部分更新。
正确做法是绕过 AR 实例,直接用静态方法 updateAll() —— 它只生成一条 SQL,不加载模型、不触发事件、不走验证,纯数据库层操作。
-
User::updateAll(['status' => 2], ['id' => [101, 102, 103]]):一次更新多行,条件支持数组、字符串、表达式 - 注意:
updateAll()不会调用beforeUpdate/afterUpdate事件,也不受TimestampBehavior影响,时间字段需显式写入,如['status' => 2, 'updated_at' => new Expression('NOW()')] - 若需保留事件或验证逻辑,必须用事务包裹循环,但应先评估是否真有必要——多数后台批处理不需要
where 条件写错会导致全表误更新
updateAll() 和 deleteAll() 没有默认 WHERE 保护,漏写条件等于“更新整张表”。线上事故高频原因。
典型错误:User::updateAll(['status' => 0]); —— 没有条件,所有用户状态变 0。
- 永远显式传入第二个参数
$condition,哪怕只是['id' => $id] - 条件值尽量用数组格式(如
['in', 'id', $ids]),避免拼接字符串引发 SQL 注入或语法错误 - 开发期可在配置中启用
'enableSchemaCache' => true和日志组件记录完整 SQL,便于快速核对生成语句 - 高危操作上线前,先用
createCommand()->update(...)->getRawSql()打印 SQL,人工确认后再执行
时间字段更新要特别小心
AR 默认不自动处理 created_at/updated_at,必须靠 TimestampBehavior 行为。但这个行为在 updateAll() 下完全不生效,在 update() 中又默认强制更新 updated_at,容易和业务逻辑冲突。
- 如果只想更新业务字段,不想动时间戳:用
update()并传入$attributeNames排除时间字段,如$model->update(false, ['name', 'email']) - 如果想用
updateAll()同时更新时间字段:必须手动加new Expression('NOW()')或time(),不能依赖行为 - 主键非
id时(如user_id),findOne()仍可用,但updateAll()的条件字段名必须与实际列名一致,别写成模型属性名
最易被忽略的是:AR 的更新逻辑高度依赖模型定义是否完整——primaryKey() 返回错误、tableName() 拼错、behaviors() 配置遗漏,都会让看似正常的调用产生静默失败或错表操作。上线前务必用最小数据集实测全流程。











