当使用相同的值更新 Mysql 表时,实际上没有任何内容受到影响,因此 rowCount 将返回 0。正如下面的 Perl 先生提到的,这并不总是首选行为,并且您可以从 PHP 5.3 开始自行更改。
只需使用以下方法创建您的 PDO 对象
<? php
$p = new PDO($dsn, $u, $p, array(PDO::MYSQL_ATTR_FOUND_ROWS => true));
?>
然后 rowCount() 将告诉您更新查询实际找到/匹配了多少行。
(PHP 5 >= 5.1.0, PHP 7, PHP 8, PECL pdo >= 0.1.0)
PDOStatement::rowCount — 返回上次 SQL 语句影响的行数
PDOStatement::rowCount() 返回由相应的 PDOStatement
对象执行的上次 DELETE、INSERT 或 UPDATE 语句影响的行数。
对于生成结果集的语句,例如 SELECT
,其行为未定义,并且每个驱动程序可能不同。某些数据库可能会返回该语句产生的行数(例如,缓冲模式下的 MySQL),但此行为并非所有数据库都保证,不应依赖于可移植应用程序。
注意:
此方法始终在 SQLite 驱动程序中返回“0”(零),并且在 PostgreSQL 驱动程序中仅在将
PDO::ATTR_CURSOR
语句属性设置为PDO::CURSOR_SCROLL
时返回。
此函数没有参数。
返回行数。
如果属性 PDO::ATTR_ERRMODE
设置为 PDO::ERRMODE_WARNING
,则发出级别为 E_WARNING
的错误。
如果属性 PDO::ATTR_ERRMODE
设置为 PDO::ERRMODE_EXCEPTION
,则抛出 PDOException。
示例 #1 返回已删除的行数
PDOStatement::rowCount() 返回 DELETE、INSERT 或 UPDATE 语句影响的行数。
<?php
/* 删除 FRUIT 表中的所有行 */
$del = $dbh->prepare('DELETE FROM fruit');
$del->execute();
/* 返回已删除的行数 */
print "Return number of rows that were deleted:\n";
$count = $del->rowCount();
print "Deleted $count rows.\n";
?>
以上示例将输出类似以下内容
Return number of rows that were deleted: Deleted 9 rows.
示例 #2 统计 SELECT 语句返回的行数
对于大多数数据库,PDOStatement::rowCount() 不会返回 SELECT 语句影响的行数。相反,请使用 PDO::query() 发出与目标 SELECT 语句具有相同谓词的 SELECT COUNT(*) 语句,然后使用 PDOStatement::fetchColumn() 检索匹配的行数。
<?php
$sql = "SELECT COUNT(*) FROM fruit WHERE calories > 100";
$res = $conn->query($sql);
$count = $res->fetchColumn();
print "There are " . $count . " matching records.";
以上示例将输出类似以下内容
There are 2 matching records.
当使用相同的值更新 Mysql 表时,实际上没有任何内容受到影响,因此 rowCount 将返回 0。正如下面的 Perl 先生提到的,这并不总是首选行为,并且您可以从 PHP 5.3 开始自行更改。
只需使用以下方法创建您的 PDO 对象
<? php
$p = new PDO($dsn, $u, $p, array(PDO::MYSQL_ATTR_FOUND_ROWS => true));
?>
然后 rowCount() 将告诉您更新查询实际找到/匹配了多少行。
太好了,在使用 MySQL5 时,执行 PDO SELECT 查询后获取行数的唯一方法是执行单独的 SELECT COUNT(*) 查询(或执行 count($stmt->fetchAll()),这似乎是一种荒谬的开销和编程时间浪费)。
我对 PDO 的另一个抱怨是它无法在某些 DBMS(例如 SQL Server)中获取存储过程的输出参数的值。
我不确定我是否还喜欢 PDO。
请注意,INSERT ... ON DUPLICATE KEY UPDATE 语句不是 INSERT 语句,对于此类语句,rowCount 不会返回插入或更新的行数。对于 MySQL,如果插入行,它将返回 1,如果更新行,它将返回 2,但这可能不适用于其他数据库。
要仅在查询不为空时显示信息,我执行以下操作
<?php
$sql = 'SELECT model FROM cars';
$stmt = $db->prepare($sql);
$stmt->execute();
if ($data = $stmt->fetch()) {
do {
echo $data['model'] . '<br>';
} while ($data = $stmt->fetch());
} else {
echo 'Empty Query';
}
?>
如果您只使用 MySQL,最好使用 SQL_CALC_FOUND_ROWS。它有很多优点,因为您可以仅检索结果集的一部分(通过 LIMIT),但仍然可以获取总行数。
代码
<?php
$db = new PDO(DSN...);
$db->setAttribute(array(PDO::MYSQL_USE_BUFFERED_QUERY=>TRUE));
$rs = $db->query('SELECT SQL_CALC_FOUND_ROWS * FROM table LIMIT 5,15');
$rs1 = $db->query('SELECT FOUND_ROWS()');
$rowCount = (int) $rs1->fetchColumn();
?>
在某些驱动程序中,rowCount() 仅在使用 PDO::CURSOR_SCROLL 的 prepare() 时有效。
因此,您可以修改 PDO 类。
<?php
class myPDO extends PDO
{
function query($query, $values=null)
{
if($query == "")
return false;
if($sth = $this->prepare($query, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL)))
{
$res = ($values) ? $sth->execute($values) : $sth->execute();
if(!$res)
return false;
}
return $sth;
}
}
?>
现在让我们测试一下(我使用 php 5.2.9-2)
<?php
function TestRowCount($dsn, $db_user, $db_pass)
{
$pdh = new PDO($dsn, $db_user, $db_pass);
$sth = $pdh->query("SELECT * FROM sys.tables");
print "rowCount() 标准: ".$sth->rowCount()."<br>";
$pdh = new myPDO($dsn, $db_user, $db_pass);
$sth = $pdh->query("SELECT * FROM sys.tables");
print "rowCount() 新版: ".$sth->rowCount()."<br><br>";
$pdh=null;
}
$db_server = "xxx";
$db_name = "xxx";
$db_user = "xxx";
$db_pass = "xxx";
print "PDO_MSSQL"."<br>";
TestRowCount("mssql:host=$db_server;dbname=$db_name", $db_user, $db_pass);
print "MSSQL 通过 PDO_ODBC"."<br>";
TestRowCount("odbc:DRIVER={SQL Server};SERVER=$db_server;DATABASE=$db_name;", $db_user, $db_pass);
print "MS SQL 驱动程序 2.0"."<br>";
TestRowCount("sqlsrv:server=$db_server;Database=$db_name", $db_user, $db_pass);
?>
我的结果
-------------------
PDO_MSSQL
rowCount() 标准: 0
rowCount() 新版: 0
MSSQL 通过 PDO_ODBC
rowCount() 标准: -1
rowCount() 新版: 53
MS SQL 驱动程序 2.0
rowCount() 标准: -1
rowCount() 新版: 53
-------------------
使用 myPDO 类,您可以使用如下准备好的查询:
<?php
$pdh = new myPDO($dsn, $db_user, $db_pass);
$sth = $pdh->query("select * from data where id>? or name like ?", array(100, "A%"));
?>
注意
=====
在使用缓冲查询的 Mysql SELECT 语句中,rowCount 将返回结果集中项目的正确数量。
但是,如果您的查询是非缓冲的,则它将返回 0。无论是否从结果集中检索所有行(而在 mysqli 中,此行为有所不同 - 您仍然会获得结果集中项目的数量非缓冲查询,但仅当您从集合中检索所有行时)。
示例
========
$conn = new PDO("mysql:host=127.0.0.1;dbname=db", 'root', 'root');
// 使用非缓冲查询
$conn->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$stmt = $conn->query("select * from towns");
echo $stmt->rowCount(); // 将始终返回 0
MySQL 似乎没有在 select 语句的 rowCount 中返回任何内容,但您可以轻松高效地获取行数,如下所示
class db extends PDO {
public function last_row_count() {
return $this->query("SELECT FOUND_ROWS()")->fetchColumn();
}
}
$myDb = new db('mysql:host=myhost;dbname=mydb', 'login', 'password' );
然后,在运行您的查询后
if ( $myDb->last_row_count() == 0 ) {
echo "执行某些操作!";
}
每一项好的工作
如果您使用“INSERT INTO ... ON DUPLICATE KEY UPDATE”语法,mysql_affected_rows() 将在进行更新时返回 2(就像“REPLACE INTO”语法一样)以及在插入时返回 1。
因此,如果您使用一个 SQL 请求一次插入多行,并且某些行被插入,某些行只是被更新,您将无法获得真实的计数。
好吧,我不会像建议的那样查询两次数据库以获取计数,然后获取我想要的数据。查询一次并同时检索记录计数和数据本身会更简单,并且性能会更好。
<?php
$sql = "SELECT * FROM fruit WHERE calories > :calories";
$sth = $conn->prepare($sql);
$sth->bindParam(':calories', 100, PDO::PARAM_INT);
$res = $sth->execute();
if ($res) {
$record = $sth->fetchAll();
/* 检查与 SELECT 语句匹配的行数 */
if (count($record) > 0) {
foreach ($record as $row) {
print "名称: " . $row['NAME'] . "\n";
}
}
/* 没有行匹配 - 执行其他操作 */
else {
print "没有行与查询匹配。";
}
}
$conn = null;
?>
请注意 PostgreSQL 的另一种有趣的行为。
如果您尝试在使用设置为 PDO::CURSOR_SCROLL 的 PDO::ATTR_CURSOR 准备语句后使用 rowCount(),则始终会获得零 (0)。
这是因为 PG 在遍历所有行之前没有任何方法来判断游标中有多少行。
<?php
$st = $pdo->prepare('SELECT NOW();', [PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL]);
$st->execute();
var_dump($st->rowCount());
?>
将返回“0”,而没有 CURSOR_SCROLL 属性的相同语句将正确返回 1。
有关详细信息,请参阅此错误报告 https://bugs.php.net/bug.php?id=77855。
此文档将很快更新以反映该问题。
从 SQLite 3.x 版本开始,SQLite API 本身发生了变化,现在所有查询都使用“语句”来实现。因此,PDO 无法知道 SELECT 结果的 rowCount,因为 SQLite API 本身不提供此功能。
作为一种变通方法,我创建了自己的 rowCount() 函数——这有点像黑客手段,尚未完全测试(我不知道它在 SELECT 中使用 JOIN 时会如何工作等等),但至少可以避免在代码中到处使用 SELECT COUNT(*)。
我更希望能够重载 PDOStatement 中的 rowCount() 函数,但我认为这是不可能的(或者我不知道如何做到)。此外,还有可能需要更多的安全性,以确保在其他 query() 后清除 $queryString,这样你就不会得到错误的结果等等……
实际代码应该发布在上述/下面的帖子中(帖子长度限制,唉!)。如果其他人希望扩展/完善此方法,请通过电子邮件告诉我你做了什么。
我们遇到了这些 PDOStatement::fetchColumn() 和 PDOStatement::rowCount() 的问题。我不知道我们是否与其他人有类似的情况,或者这仅仅是我们编码中的问题。在本地,rowCount() 没有给出正确的行数,但在上传到我们的托管站点时却可以正常工作……而 fetchColumn() 则相反。fetchColumn() 在本地工作正常,但上传后就不再正常了。我不知道到底发生了什么,但我认为 rowCount() 是最好的,其他的都是可选的。