【发布时间】:2012-05-11 04:30:30
【问题描述】:
此查询在 phpMyAdmin 中返回 5 个结果:
SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT 0,5
这会在 phpMyAdmin 中返回 count=12(这很好,因为有 12 条记录):
SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT 0,5
在我添加两个变量(偏移量、显示)之前,这个函数运行良好,但是现在它不起作用,并且打印出变量给我偏移量=0,显示=5(所以仍然是LIMIT 0,5)。
function getProducts($offset, $display) {
$sql = "SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT ?,?;";
$data = array((int)$offset, (int)$display);
$rows = dbRowsCount($sql, $data);
logErrors("getProducts(".$offset.",".$display.") returned ".$rows." rows.");
if ($rows > 0) {
dbQuery($sql, $data);
return dbFetchAll();
} else {
return null;
}
}
它不起作用,因为我的 dbRowsCount(...) 方法返回的是空字符串(愚蠢的 PDOStatement::fetchColumn),所以我将其更改为使用 PDO::FETCH_ASSOC 返回计数并返回 count=0。
这里是计算行数的函数:
function dbRowsCount($sql, $data) {
global $db, $query;
$regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/is';
if (preg_match($regex, $sql, $output) > 0) {
$query = $db->prepare("SELECT COUNT(*) AS count FROM {$output[1]}");
logErrors("Regex output: "."SELECT COUNT(*) AS count FROM {$output[1]}");
$query->setFetchMode(PDO::FETCH_ASSOC);
if ($data != null) $query->execute($data); else $query->execute();
if (!$query) {
echo "Oops! There was an error: PDOStatement returned false.";
exit;
}
$result = $query->fetch();
return (int)$result["count"];
} else {
logErrors("Regex did not match: ".$sql);
}
return -1;
}
我的错误日志给了我这个程序的输出:
正则表达式输出:SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT ?,?;
getProducts(0,5) 返回 0 行。
如您所见,SQL 并没有畸形,方法输入变量如预期的那样为 0 和 5。
有谁知道出了什么问题?
更新
根据建议,我确实尝试直接执行查询,它返回了正确的结果:
function dbDebugTest() {
global $db;
$stmt = $db->query("SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update LIMIT 0,5;");
$result = $stmt->fetch();
$rows = (int)$result["count"];
logErrors("dbDebugTest() returned rows=".$rows);
}
输出:
> dbDebugTest() returned rows=12
根据另一个建议,我将 !=null 更改为 !==null,并且还打印了 $data 数组:
logErrors("Data: ".implode(",",$data));
if ($data !== null) $query->execute($data); else $query->execute();
输出:
> Data: 0,5
但是,dbRowsCount($sql, $data) 仍然为此查询返回 0 行!
更新 2
遵循实现a custom PDOStatement class 的建议,这将允许我在绑定值后输出查询,我发现该函数将在 $query->execute($data) 之后停止,因此不会打印输出,尽管自定义类适用于我程序中的所有其他查询。
更新代码:
function dbRowsCount($sql, $data) {
global $db, $query;
$regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/is';
if (preg_match($regex, $sql, $output) > 0) {
$query = $db->prepare("SELECT COUNT(*) AS count FROM {$output[1]}");
logErrors("Regex output: "."SELECT COUNT(*) AS count FROM {$output[1]}");
$query->setFetchMode(PDO::FETCH_ASSOC);
logErrors("Data: ".implode(",",$data));
$query->execute($data);
logErrors("queryString:".$query->queryString);
logErrors("_debugQuery():".$query->_debugQuery());
if (!$query) {
echo "Oops! There was an error: PDOStatement returned false.";
exit;
}
$result = $query->fetch();
return (int)$result["count"];
} else {
logErrors("Regex did not match: ".$sql);
}
return -1;
}
输出:
正则表达式输出:SELECT COUNT() AS count FROM tbl_product_category WHERE id=?;
数据:5
queryString:SELECT COUNT() AS count FROM tbl_product_category WHERE id=?;
_debugQuery():SELECT COUNT(*) AS count FROM tbl_product_category WHERE id=?;正则表达式输出:SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT ?,?;
数据:0,5
// 函数停止,_debugQuery 无法输出
更新 3
由于我无法让自定义 PDOStatement 类给我一些输出,我想我会重写 getProducts(...) 类来将参数与命名占位符绑定。
function getProducts($offset, $display) {
$sql = "SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT :offset, :display;";
$data = array(':offset'=>$offset, ':display'=>$display);
$rows = dbRowsCount($sql, $data);
logErrors("getProducts(".$offset.",".$display.") returned ".$rows." rows.");
if ($rows > 0) {
dbQuery($sql, $data);
return dbFetchAll();
} else {
return null;
}
}
输出:
正则表达式输出:SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT :offset, :display;
数据:0,5
// 在 $query->execute($data) 之后仍然崩溃,所以logErrors("getProducts(".$offset."...))没有被打印出来
更新 4
此 dbDebugTest 以前使用直接在 SQL 字符串中声明限值 0,5。现在我已经更新它以正确绑定参数:
function dbDebugTest($offset, $display) {
logErrors("Beginning dbDebugTest()");
global $db;
$stmt = $db->prepare("SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update LIMIT :offset,:display;");
$stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
$stmt->bindParam(':display', $display, PDO::PARAM_INT);
if ($stmt->execute()) {
$result = $stmt->fetch();
$rows = (int)$result["count"];
logErrors("dbDebugTest() returned rows=".$rows);
} else {
logErrors("dbDebugTest() failed!");
}
}
函数崩溃,只有这个输出:
开始 dbDebugTest()
更新 5
按照打开错误的建议(默认情况下它们是关闭的),我这样做了:
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
它本身使更新 4 中的 dbDebugTest() 工作!
开始 dbDebugTest() dbDebugTest() 返回 rows=12
现在我的网络服务器日志中生成了一个错误:
[警告] mod_fcgid:stderr:PHP 致命错误:
未捕获的异常“PDOException”,带有消息“SQLSTATE[42000]:
语法错误或访问冲突:1064 您的 SQL 语法有错误;
检查与您的 MySQL 服务器版本相对应的手册,以获取正确的语法使用
靠近第 1 行的“0”、“5”
在/home/linweb09/b/example.com-1050548206/user/my_program/database/dal.php:36
第 36 行引用dbRowsCount(...) 方法,该行是$query->execute($data)。
所以另一种方法getProducts(...) 仍然不起作用,因为它使用这种方法绑定数据并且参数变成了''0'和'5''(这是一个错误吗?)。有点烦人,但我必须在我的 dal.php 中创建一个新方法,以允许自己以更严格的方式绑定参数 - 使用 bindParam。
特别感谢@Travesty3 和@eggyal 的帮助!!非常非常感谢。
【问题讨论】:
-
您是否尝试手动运行查询?喜欢在没有任何参数或包装函数的情况下使用
$db->query(xxx)?那它有用吗? -
我不明白为什么这被否决了? @kuba 我没有,因为我直接在 phpMyAdmin 中尝试了 SQL,所以我认为它会起作用,但我现在会尝试。
-
@Ozzy:我也不理解反对票(你有我的 +1)。您是否测试过
if ($data != null)以查看执行的路径?不应该在 PHP 中使用!is_null()或!==吗? -
@eggyal:应该使用
!empty()。 -
kuba,是的,它返回 rows=12。我有一种感觉限制参数没有通过关联数组绑定。 @eggyal 我确实尝试过,并且 $data 不为空 p.s 感谢您的支持