mySQL PDO准备声明来自非准备声明的不同结果

时间:2016-03-09 19:18:32

标签: php mysql pdo prepared-statement

我对PDO的体验有限,而且我已经坚持了一段时间。问题是,当我运行未准备好的代码时(因为这已被证明是我可以调试PDO的唯一方法),我得到了我想要的结果。当我把它作为准备好的声明运行时,我会得到不同的结果。见下文:

未准备 代码:

$interval = array("hourly" => "1 HOUR", "daily" => "1 DAY", "weekly" => "7 DAY", "monthly" => "30 DAY", "yearly" => "1 YEAR");
$intervalString = "INTERVAL " . $interval[$p_sLimitType];

$SQL = "SELECT COUNT(*) as `counted` FROM tbl_transaction" .
       " WHERE type='" . $p_sPostType . "'" . 
       " AND catID=" . $p_nCatID .
       " AND serviceID=" . $p_nServiceID .
       " AND serviceIdentity=" . $p_nServiceUserID .
       " AND timestamp BETWEEN DATE_SUB(NOW(), $intervalString . ") AND NOW()";

$theQuery = $DB->Query($SQL);
echo "\r\n\r\nQuery:";
print_r($theQuery);

echo "\r\nResult:";
$result = $theQuery->fetch(PDO::FETCH_ASSOC);
print_r($result);

未准备 结果:

Query:PDOStatement Object
(
    [queryString] => SELECT COUNT(*) as `counted` FROM tbl_transaction WHERE type='pudding' AND catID=13 AND serviceID=1 AND serviceIdentity=3324848959 AND timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 DAY) AND NOW()
)

Result:Array
(
    [counted] => 15
)

现在 准备好的代码

$interval = array("hourly" => "1 HOUR", "daily" => "1 DAY", "weekly" => "7 DAY", "monthly" => "30 DAY", "yearly" => "1 YEAR");
$intervalString = "INTERVAL " . $interval[$p_sLimitType];

$SQL = "SELECT COUNT(*) as `counted` FROM tbl_transaction" .
       " WHERE type=:postType" . 
       " AND catID=:catID" .
       " AND serviceID=:serviceID" .
       " AND serviceIdentity=:serviceIdentity" .
       " AND timestamp BETWEEN DATE_SUB(NOW(), :interval) AND NOW()";

// Execute the statement

try { 
    $stmt = $DB->prepare($SQL);
    $stmt->bindParam(':postType', $p_sPostType, PDO::PARAM_STR, 30);
    $stmt->bindParam(':catID', $p_nCatID, PDO::PARAM_INT);
    $stmt->bindParam(':serviceID', $p_nServiceID, PDO::PARAM_INT);
    $stmt->bindParam(':serviceIdentity', $p_nServiceUserID, PDO::PARAM_INT);
    $stmt->bindParam(':interval', $intervalString, PDO::PARAM_STR, 30);
    $result = $stmt->execute();
} catch(PDOException $e) {
    mm_die($e->getMessage());
}

echo "\r\n\$SQL = $SQL";
//          echo "\r\n\$p_nLimitValue = $p_nLimitValue\r\n";
echo "\r\nRow Count: " .$stmt->rowCount() . "\r\n";

准备 结果:

$SQL = SELECT COUNT(*) as `counted` FROM tbl_transaction WHERE type=:postType AND siloID=:siloID AND serviceID=:serviceID AND serviceIdentity=:serviceIdentity AND timestamp BETWEEN DATE_SUB(NOW(), :interval) AND NOW()
Row Count: 0

注意"行数"准备好的陈述中为零。我盯着这看的时间比我承认的还多。任何人都可以看到为什么一个返回结果而另一个没有?谢谢!

3 个答案:

答案 0 :(得分:3)

问题是DATE_SUB()的第二个参数必须是一个区间,但是你提供了一个字符串。字符串"INTERVAL 1 HOUR不会自动转换为相应的间隔。您只能将占位符用于INTERVAL表达式的数字部分,而不能用于关键字。

将时间单位从关联数组中取出,并将所有内容表示为小时。

$interval = array("hourly" => 1, "daily" => 24, "weekly" => 7*24, "monthly" => 30*24, "yearly" => 365*24);

然后你可以这样做:

$SQL = "SELECT COUNT(*) as `counted` FROM tbl_transaction" .
       " WHERE type=:postType" . 
       " AND catID=:catID" .
       " AND serviceID=:serviceID" .
       " AND serviceIdentity=:serviceIdentity" .
       " AND timestamp BETWEEN DATE_SUB(NOW(), INTERVAL :interval HOUR) AND NOW()";

$stmt->bindParam(':interval', $interval[$p_sLimitType], PDO::PARAM_INT);

答案 1 :(得分:1)

占位符不能代表查询的任意部分,而只能代表完整的字符串或数字文字。

因此你不能绑定一部分间隔。

只要您在上面显示的代码中有列入白名单的时间间隔,您就可以并且必须坚持使用旧方法进行间隔。

答案 2 :(得分:0)

select count(*)无法返回0行。它将始终返回至少一个行,其中包含匹配/找到的行的计数。获取0行意味着您的查询完全失败,并且没有返回结果集,句点。

您是否在PDO中启用了例外?默认情况下,它会因失败而“返回false”,并且除非您明确启用它们,否则不会抛出异常。如果他们没有启用,那么你的try/catch就没用了。