应避免用rowCount()判断SELECT结果是否为空,它在MySQL PDO中通常返回-1或上一条非SELECT语句影响行数;正确做法是fetch一行判false,兼顾性能与兼容性。

PHP中用rowCount()判断PDO查询结果是否为空,但仅限SELECT以外的语句
很多人误以为rowCount()能可靠反映SELECT语句查到了几行——它在MySQL PDO中对SELECT返回的是“上一次执行的INSERT/UPDATE/DELETE影响行数”,不是查询结果集大小。只有在启用PDO::MYSQL_ATTR_FOUND_ROWS且驱动支持时才可能例外,但不可靠。
真正安全的做法是:对SELECT,别依赖rowCount();对INSERT/UPDATE/DELETE,它才是正解。
-
SELECT后调用rowCount():MySQL PDO默认返回-1(未缓存结果集),即使查到100行也可能是-1 -
DELETE FROM users WHERE id = 123后调用rowCount():返回1或0,表示是否真删掉了数据 - 若强制用
fetchAll()取全部结果再count(),会把整张表加载进内存,大结果集直接OOM
MySQLi中num_rows可用,但必须先调用store_result()或使用mysqli_use_result()
num_rows属性看起来直观,但它不是“随时可读”的——对于面向对象风格的mysqli_stmt,它只在执行execute()之后、且结果集已缓冲到客户端时才有效。否则返回0或null,不是真的没数据,而是还没准备好。
- 用
mysqli_prepare()+mysqli_stmt_execute()后,必须立刻调用mysqli_stmt_store_result(),否则$stmt->num_rows为0 - 用
mysqli_query()(非预处理)得到mysqli_result对象,则$result->num_rows可直接读,无需额外操作 - 如果用
mysqli_use_result()流式读取,num_rows不可用,因为服务端还没发完,客户端根本不知道总数
最稳妥的通用方案:fetch一行再判断是否false
不依赖元信息,直接尝试取第一行。既避免内存膨胀,又绕过驱动差异和缓冲陷阱,兼容PDO/MySQLi/SQLite所有场景。
立即学习“PHP免费学习笔记(深入)”;
- PDO:
$stmt->fetch()返回数组或false;若需保持游标位置,用$stmt->fetch(PDO::FETCH_NUM)+$stmt->execute()重试 - MySQLi(预处理):
$stmt->fetch()绑定变量后判断是否true;或mysqli_stmt_fetch()返回int(1=成功,0=无数据) - 简单场景直接
if ($row = $stmt->fetch()) { /* 有数据 */ } else { /* 为空 */ } - 注意:不要在循环里反复
fetch()只为了判空,fetch一次就够了
为什么不用SELECT COUNT(*)提前检查?
看似精准,实则多一次网络往返+全表扫描(无索引时),性能开销远超直接查数据。尤其当业务逻辑本就要取真实记录时,纯属浪费。
- 例如“用户登录验证”:先
COUNT(*)查是否存在,再SELECT * FROM user取详情 → 两次查询,锁两次,延迟翻倍 - 正确做法:一条
SELECT id, name, pwd_hash FROM user WHERE email = ?,fetch一次,有就继续,无就报错 - 只有极少数场景需要精确行数(如分页总数),才单独用
COUNT,且务必加覆盖索引
实际写代码时,最容易被忽略的是预处理语句下num_rows的“延迟可用性”——很多开发者在execute()后立刻读num_rows,得到0就认为没数据,其实只是结果集还没缓冲完。



















