在使用 Yii 1.1 的 CDbCommand 執行寫操作時,開發者習慣用返回值判斷成功與否:
$affected = Yii::app()->db->createCommand()
->update('emg_landing', ['status' => 1], 'lid=:lid', [':lid' => $id]);
if ($affected > 0) {
returnApi::success('更新成功');
} else {
returnApi::error('更新失敗'); // 💥 這裡誤判!
}
現象:數據實際更新了,但代碼進入了「失敗」分支。排查半天無果,翻閱官方文檔才恍然大悟。
| 方法 | 返回值含義 | 判斷成功的正確條件 |
|---|---|---|
insert() | 受影響行數(通常為 1) | >= 1 即成功 |
delete() | 受影響行數(刪除的行數) | >= 1 即成功 |
update() | 真正被修改的行數 | >= 0 均可能成功! |
>= 1 直接等價於成功。WHERE 條件命中行數 ≠ 實際修改行數。若 SET col = val 中 val 與原值相同,MySQL 不會真正寫入磁盤,該行不計入「受影響行數」,返回 0。但 SQL 執行本身是成功的,數據邏輯上也「更新為相同值」。<?php
// 假設表 user(id PK, name, status) 有一條 id=1, name='Tom', status=1
$db = Yii::app()->db;
// 情況 A:更新為不同值
$r1 = $db->createCommand()->update('user', ['status' => 2], 'id=1');
var_dump($r1); // int(1) 真正修改了
// 情況 B:更新為相同值
$r2 = $db->createCommand()->update('user', ['status' => 2], 'id=1');
var_dump($r2); // int(0) 數據無變化,但 SQL 執行成功!
// 情況 C:WHERE 不命中任何行
$r3 = $db->createCommand()->update('user', ['status' => 3], 'id=999');
var_dump($r3); // int(0) 同樣返回 0,但語義是「無匹配行」
<?php
/**
* 安全執行更新並正確判斷結果
* @return array ['success' => bool, 'info' => string, 'affected' => int]
*/
function safeUpdate($table, $columns, $condition, $params = []) {
$cmd = Yii::app()->db->createCommand();
$affected = $cmd->update($table, $columns, $condition, $params);
$error = $cmd->getDbConnection()->errorInfo(); // [SQLSTATE, driverCode, driverMsg]
if ($error[0] !== '00000') {
return ['success' => false, 'info' => 'SQL錯誤: '.$error[2], 'affected' => $affected];
}
// 執行無異常即視為成功,affected=0 僅表示「無實際數據變更」
return ['success' => true, 'info' => $affected > 0 ? '數據已變更' : '數據無變化(或條件不匹配)', 'affected' => $affected];
}
// 使用示例
$res = safeUpdate('emg_landing', ['status' => 1], 'lid=:lid', [':lid' => $id]);
if ($res['success']) {
returnApi::success($res['info']);
} else {
returnApi::error($res['info']);
}
若業務強制要求區分(如「用戶不存在」vs「狀態已是目標值」),需額外查詢:
function updateWithDetail($table, $columns, $condition, $params) {
$db = Yii::app()->db;
// 1. 先查是否存在匹配行
$exists = $db->createCommand()
->select('COUNT(*)')
->from($table)
->where($condition, $params)
->queryScalar();
if (!$exists) {
return ['code' => 'NOT_FOUND', 'msg' => '記錄不存在'];
}
// 2. 執行更新
$affected = $db->createCommand()->update($table, $columns, $condition, $params);
return $affected > 0
? ['code' => 'UPDATED', 'msg' => '更新成功']
: ['code' => 'NO_CHANGE', 'msg' => '數據已是目標值,無需更新'];
}
delete() 返回刪除行數。若 WHERE 不命中,返回 0,此時 語義明確為「無匹配行可刪」,通常視為業務失敗(如「取消訂單」時訂單已不存在)。但若業務允許「冪等刪除」,則 0 也可視為成功。需根據具體業務定義。
| 場景 | 推薦判斷邏輯 |
|---|---|
| insert | $affected >= 1 成功,否則拋異常/查錯誤碼 |
| delete | 視業務:強一致性要求 >=1;冪等允許 >=0 並檢查 SQLSTATE |
| update | 永不直接用 affected 判斷成敗。檢查 SQLSTATE == '00000' 即成功,affected 只作日誌/統計用 |
💡 核心心法:受影響行數 ≠ 執行成敗。Yii/CDbCommand 只是透傳 PDO::exec() 返回值,PDO 文檔明確說明:「For UPDATE statements, the return value is the number of rows actually changed.」 理解數據庫引擎層面的「修改」定義,才能寫出不踩坑的代碼。