使用相同的更新值更新 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”(零),并且仅在将
PDO::ATTR_CURSOR
语句属性设置为PDO::CURSOR_SCROLL
时才会使用 PostgreSQL 驱动程序返回“0”。
此函数没有参数。
返回行数。
如果属性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 "返回已删除的行数:\n";
$count = $del->rowCount();
print "删除了 $count 行。\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 "共有 " . $count . " 个匹配记录。";
上面的示例将输出类似于以下内容
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() 仅在使用 prepare() 与 PDO::CURSOR_SCROLL 时才有效
因此,您可以修改 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() Standart: ".$sth->rowCount()."<br>";
$pdh = new myPDO($dsn, $db_user, $db_pass);
$sth = $pdh->query("SELECT * FROM sys.tables");
print "rowCount() New: ".$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 throw PDO_ODBC"."<br>";
TestRowCount("odbc:DRIVER={SQL Server};SERVER=$db_server;DATABASE=$db_name;", $db_user, $db_pass);
print "MS SQL driver 2.0"."<br>";
TestRowCount("sqlsrv:server=$db_server;Database=$db_name", $db_user, $db_pass);
?>
我的结果
-------------------
PDO_MSSQL
rowCount() Standart: 0
rowCount() New: 0
MSSQL throw PDO_ODBC
rowCount() Standart: -1
rowCount() New: 53
MS SQL driver 2.0
rowCount() Standart: -1
rowCount() New: 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 似乎没有在 rowCount 中为 select 语句返回任何内容,但你可以轻松高效地获取行数,如下所示
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 "Do something!";
}
每一项好的工作
如果你使用 "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 "Name: " . $row['NAME'] . "\n";
}
}
/* 没有行匹配 - 执行其他操作 */
else {
print "No rows matched the query.";
}
}
$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() 函数,但我认为这是不可能的(或者我不知道如何做到)。还有可能在确保 $queryString 在其他 query() 后被清除方面有更多安全措施,这样你就不会得到错误的结果等等。
实际代码应发布在上面的/下面的帖子中(帖子最大限制,啊!)。如果其他人希望扩展/完善此方法,请通过电子邮件告知我您所做的事情。
我们在使用 PDOStatement::fetchColumn() 和 PDOStatement::rowCount() 时遇到了问题。我不知道我们是否遇到与其他人相同的情况,或者这仅仅是我们代码中的问题。在本地,rowCount() 没有给出正确的行数,但在上传到我们的托管网站时它工作正常。而 fetchColumn() 则相反。fetchColumn() 在本地工作正常,但在上传后就不再工作了。我不知道究竟发生了什么,但我认为 rowCount() 是最好的,而其他方法是可选的。
另一个解决方法是在一个 SELECT 语句中返回行数(请参阅下面的限制!)。
$sth = $dbh->prepare("SELECT *,count(*) AS howmany FROM users WHERE email=:email and password=:pass"); #变量占位符
$sth->execute(array(':email'=>$email, ':pass'=>$pass)); #变量绑定
$row = $sth->fetch(); #获取一行(始终只有一行!!!)
if ($row['howmany'] == 1){ #匹配并只有一行!很酷!
echo $row['email'], $row['name'], $row['phone'], ... ;
} elseif ($row['howmany']>1) { #返回多行
#应该开除一个程序员,因为他没有检查
#是否存在电子邮件,然后创建新用户
...
#以某种方式处理此异常,或者直接跳过此分支,
#如果您确定您的表中不会发生这种情况
} else { #表中没有匹配项($row['howmany'] == 0)
echo "电子邮件/密码与数据库中的不匹配!";
}
优点
- 只执行一个 SELECT 语句,不需要两步!
- 它根据 WHERE 子句检查表中是否存在一行。
- 如果存在,它将返回该行的所有字段(或仅选定的字段)。
缺点
- 如果找到多行,它不会可靠地返回行字段。如果多个行响应 SELECT 查询,则查询仍然只返回一行,您不知道具体是哪一行。
也许使用 SORT BY 会使其更具可预测性(例如:“如果找到多个用户,则返回表中最后添加的用户”),但这更多是填充表初始数据的程序的良好设计的范畴。
用途
- 它非常适合检查用户/密码对是否存在于用户表中,以及如果找到用户,则返回用户的其他字段(例如姓名、电话号码等)。
我的 rowCount() 解决方法及其用法
<?php
class MyPDO extends PDO {
private $queryString;
public function query(/* ... */) {
$args = func_get_args();
$this->queryString = func_get_arg(0);
return call_user_func_array(array(&$this, 'parent::query'), $args);
}
public function rowCount() {
$regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/i';
if (preg_match($regex, $this->queryString, $output) > 0) {
$stmt = parent::query("SELECT COUNT(*) FROM {$output[1]}", PDO::FETCH_NUM);
return $stmt->fetchColumn();
}
return false;
}
}
$pdo = new MyPDO("sqlite::memory:");
$result = $pdo->query("SELECT row1, row2 FROM table WHERE something = 5");
if ($pdo->rowCount() > 0) {
echo "{$result['row1']}, {$result['row2']}";
}
?>