update() 必须配合 where() 使用,参数顺序为$table、$columns(键值对数组)、$condition(支持数组或字符串),返回command对象需调用execute()执行并获取影响行数。

update() 方法的基本用法和参数顺序
Yii2 查询构造器的 update() 不是链式调用的终点,它必须配合 where() 使用,否则会报错或误更新全表。它的签名是
update($table, $columns, $condition = '', $params = []) <p>注意:第二个参数 <code>$columns</code></p>是键值对数组,键是字段名(不加表前缀),值是要更新的值;第三个参数
$condition 是 WHERE 条件,支持数组格式(推荐)或字符串。
常见错误现象:SQLSTATE[HY000]: General error: 1364 Field 'xxx' doesn't have a default value——说明漏写了必填字段,但更大概率是你误用了 insert() 或没传 where 导致语法生成异常。
-
$columns中的值会被自动参数化,不用手动转义 - 如果要设为 NULL,写
'status' => null即可,框架会正确处理 - 条件用数组时,
['id' => 123]等价于WHERE id = 123;复合条件如['AND', ['status' => 0], ['created_at' => $time]]
用 Command 对象执行 update() 并获取影响行数
查询构造器本身不执行 SQL,update() 返回的是 yii\db\Command 对象,必须显式调用 execute() 才真正提交:
$rows = Yii::$app->db->createCommand()
->update('user', ['status' => 1, 'updated_at' => time()], ['id' => $id])
->execute();
返回值 $rows 是受影响的行数(int),可用于判断是否真有数据被更新。这点容易被忽略——很多人只调用 update() 就以为完成了。
- 若
$rows === 0,可能是 WHERE 条件没匹配到任何记录,也可能是新旧值完全一样(MySQL 的 safe-update 模式下不会触发变更) - 执行失败会抛出
yii\db\Exception,建议包裹在 try-catch 中 - 不建议省略
execute()直接链式“结束”,那只是构建命令,没发出去
带表达式更新(比如自增、函数计算)
直接传 PHP 变量会当字面量处理,无法实现 views = views + 1 这类操作。要用 yii\db\Expression 包裹:
use yii\db\Expression;
// 正确:生成 `score = score + 10`
Yii::$app->db->createCommand()->update('post', [
'score' => new Expression('score + 10'),
'updated_at' => new Expression('NOW()'),
], ['id' => $id])->execute();
常见错误是写成 'score' => 'score + 10',这会让框架把它当字符串字面量插入,结果变成文本而非计算。
-
new Expression()内容不经过参数绑定,需自行确保安全(别拼接用户输入) - MySQL 函数如
NOW()、UNIX_TIMESTAMP(),PostgreSQL 的NOW()都得走 Expression - 想用
CONCAT拼字段?也得new Expression("CONCAT(name, '_bak')")
批量更新与性能边界
查询构造器的 update() 一次只生成一条 UPDATE 语句,不支持原生批量更新(如 MySQL 的 UPDATE ... VALUES (...), (...))。想批量改多条不同数据,只能循环执行,或改用原生 SQL + batchInsert() 思路模拟。
但要注意:循环中反复创建 createCommand() 开销小,真正慢的是网络往返。100 行以内通常没问题;超过 500 行建议拆成事务 + 分批(比如每 100 条 commit 一次)。
- 不要在循环里重复写
Yii::$app->db->createCommand()->update(...)->execute()而不捕获异常,某条失败会导致后续中断 - 涉及关联表或复杂逻辑时,优先考虑 ActiveRecord 的
save(),它会触发事件、验证、钩子;纯数据搬运才用 Query Builder - 如果 WHERE 条件字段没有索引,批量更新可能锁表,线上慎用
execute() 或把表达式当字符串传——这两个点不检查日志根本看不出问题。











