在 PHP 的数据库抽象层 PDO(PHP Data Objects)中,PDOStatement::rowCount() 是一个常用方法,用于返回上一次 SQL 执行所影响的行数。然而,很多开发者在不同数据库驱动中使用时会发现该方法的行为并不一致,甚至在某些场景下根本得不到预期结果。
根据 PHP 官方文档 的说明:
对于大多数数据库,rowCount() 仅适用于 DELETE、INSERT 或 UPDATE 语句。对于 SELECT 语句,某些数据库驱动可能会返回受影响的行数,但这并不是 PDO 统一行为的一部分。
也就是说,rowCount() 的行为取决于底层的数据库驱动实现。
数据库类型 | 对 SELECT 语句支持 rowCount | 对 UPDATE/DELETE/INSERT 支持 rowCount |
---|---|---|
MySQL | ? 不可靠(通常返回 0) | ? 返回受影响行数 |
PostgreSQL | ? 支持 | ? 支持 |
SQLite | ? 支持 | ? 支持 |
MSSQL | ?(依赖驱动实现) | ? |
举个例子,在 MySQL 中:
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->query("SELECT * FROM users");
echo $stmt->rowCount(); // 通常返回 0
但在 PostgreSQL 或 SQLite 中,可能会返回实际的行数。
主要原因在于数据库驱动对 SQL 的执行方式不同,特别是对于 SELECT,有的驱动不会提前加载所有结果,而是边遍历边取数据,这种情况下它就无法提前知道有多少行。
MySQL 官方驱动(mysqlnd)明确指出:对于 SELECT,rowCount() 不应该被依赖。
$stmt = $pdo->query("SELECT * FROM users");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$count = count($rows);
echo "共 {$count} 行数据";
或者:
$count = 0;
$stmt = $pdo->query("SELECT * FROM users");
while ($row = $stmt->fetch()) {
$count++;
}
echo "共 {$count} 行数据";
注意:如果数据量大,fetchAll() 会占用大量内存,建议使用循环计数。
$stmt = $pdo->prepare("UPDATE users SET status = 'active' WHERE last_login > NOW() - INTERVAL 7 DAY");
$stmt->execute();
echo "更新了 " . $stmt->rowCount() . " 行";
这类操作的 rowCount() 通常是可靠的。
可以封装自己的 rowCount 函数,判断 SQL 类型后选择合适的统计方式:
function safeRowCount(PDOStatement $stmt, $sqlType = 'SELECT') {
if (strtoupper($sqlType) === 'SELECT') {
return count($stmt->fetchAll());
} else {
return $stmt->rowCount();
}
}
这样可以在不同数据库驱动中尽量保持行为一致。
PDOStatement::rowCount() 的行为不一致是 PHP 数据库开发中常见的坑之一。理解其底层机制,结合 SQL 类型选择合适的处理方式,是写出兼容性强代码的关键。
如需参考更多示例代码或分享经验,可以访问我们的技术社区:https://gitbox.net/forum。