在使用PHP 的PDO(PHP Data Objects)進行數據庫開發時, PDOStatement::rowCount()常被用於獲取查詢所影響的行數,尤其是在執行UPDATE 、 DELETE等語句之後。然而,在某些場景下,它的返回結果可能讓人感到“誤導”甚至產生bug。
本文將深入探討為什麼rowCount()有時不如我們預期,揭示它背後的原因,並給出正確應對方式。
$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password');
$stmt = $pdo->prepare("DELETE FROM users WHERE id = :id");
$stmt->execute([':id' => 123]);
echo $stmt->rowCount(); // 輸出被刪除的行數
在這段代碼中,我們刪除了一條記錄,並希望通過rowCount()知道有幾條記錄被影響。對於DELETE和UPDATE語句,這通常能正常工作。然而,一旦涉及到SELECT語句或一些特殊情況,問題就來了。
雖然rowCount()被某些數據庫(如PostgreSQL)支持用於SELECT查詢,但MySQL 中SELECT後調用rowCount() 通常會返回0 。原因是PDO 在MySQL 中默認使用的是MYSQL_ATTR_USE_BUFFERED_QUERY ,行是延遲提取的,rowCount 無法準確得知行數。
$stmt = $pdo->query("SELECT * FROM users");
echo $stmt->rowCount(); // 在 MySQL 中可能輸出 0
即使語句是合法執行的,只要數據庫中沒有實際變動, rowCount()返回的就是0。例如:
$stmt = $pdo->prepare("UPDATE users SET name = :name WHERE id = :id");
$stmt->execute([':name' => 'Alice', ':id' => 123]);
echo $stmt->rowCount(); // 如果 name 已是 Alice,則返回 0
這不是錯誤,而是SQL 的特性:沒有實際改變,就不算“影響”行。
不同的數據庫驅動實現對rowCount()的支持情況不一致。例如:
MySQL: SELECT不返回正確值, UPDATE/DELETE支持。
PostgreSQL:大多數語句支持。
SQLite:大多數語句支持。
Oracle:行為更加複雜,需小心使用。
$stmt = $pdo->query("SELECT * FROM users WHERE status = 'active'");
$rows = $stmt->fetchAll();
echo count($rows);
或者使用遍歷:
$count = 0;
foreach ($stmt as $row) {
$count++;
}
echo $count;
$stmt = $pdo->prepare("UPDATE users SET name = :name WHERE id = :id AND name != :name");
$stmt->execute([':name' => 'Alice', ':id' => 123]);
echo $stmt->rowCount(); // 只有當 name 實際變更才返回 1
在很多情況下,尤其是查詢或批量操作中,不如直接讓SQL 返回影響行數:
$stmt = $pdo->query("SELECT COUNT(*) FROM users WHERE status = 'active'");
$count = $stmt->fetchColumn();
echo $count;
永遠不要假設所有驅動的行為一致。你可以在初始化PDO 時用以下方法檢測或設置兼容性:
$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password', [
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true
]);
PDOStatement::rowCount()雖然是一個常用的接口,但它在不同數據庫和不同SQL 類型下的行為並不總是如你所願。作為開發者,了解它的適用範圍和局限性是避免踩坑的關鍵。
最安全的策略是:
避免在SELECT查詢中依賴rowCount() ;
對於UPDATE/DELETE等操作,理解“影響行”並非“找到行”;
若需精確控製或跨數據庫兼容性,考慮使用SQL 級別的計數邏輯。