【问题标题】:PDO prepared statements IN clause with named placeholders doesn't work as expected [duplicate]带有命名占位符的 PDO 准备好的语句 IN 子句无法按预期工作 [重复]
【发布时间】:2015-07-10 17:47:13
【问题描述】:

假设在一个场景中:$ids = 2,3。但由于某种原因,记录被返回,好像:$ids = 2

我相信完整代码的这一行存在一些问题,因为当我回显$ids时,它返回2,3,但实际查询返回好像只有一个id。 IE。它只返回一门课程的事件(id 2)。

$statement->execute(array("ids" => $ids, 'timestart' => $timestart, 'timeend' => $timeend))) {

我的完整代码:

$ids = array_map(function($item) { return $item->id; }, $entitlementsVOs);
$ids = implode(', ', $ids); //echo shows 2,3

$timestart = isset($_GET['start']) && $_GET['start'] != "" ? $_GET['start'] : null;
$timeend = isset($_GET['end']) && $_GET['end'] != "" ? $_GET['end'] : null;

$statement = $this->connection->prepare("SELECT name AS title, timestart AS start, timestart + timeduration AS end FROM event WHERE courseid IN(:ids) AND timestart >= :timestart AND timestart + timeduration <= :timeend");
$statement->setFetchMode(\PDO::FETCH_CLASS, get_class(new EventVO()));


if($statement->execute(array("ids" => $ids, 'timestart' => $timestart, 'timeend' => $timeend))) {
    return $statement->fetchAll();
} else {
    return null;
}

附言在 mysql 工作台中手动运行此查询返回我的两条记录(1 来自课程 id 2,另一条来自课程 3),其中我用 (2,3) 替换运算符,但在 php 执行中我只得到一条记录。

如果我对 php courseid IN(2,3) 中的值进行硬编码,例如:

$statement = $this->connection->prepare("SELECT name AS title, timestart AS start, timestart + timeduration AS end FROM event WHERE courseid IN(2,3) AND timestart >= :timestart AND timestart + timeduration <= :timeend");

我得到了我的期望,所以我认为implodeIN 运算符或$statement-&gt;execute 存在一些问题。

编辑:

我已经阅读了欺骗,但我不知道从哪里开始使用命名占位符来完成相同的操作。我的问题是,当我同时命名参数和 IN 运算符时,受骗者仅将 IN 运算符与位置占位符一起使用。

编辑 2

我已经阅读了第二个骗局,它不相关,我的问题同时使用了 IN 和命名占位符,链接的问题没有解决这个问题,但是,我现在在答案中找到了解决方案。

【问题讨论】:

  • 您需要尽可能多的占位符来绑定 IN 子句中的值!
  • 您的$ids 作为字符串传递给 MySQL,而不是转换为 MySQL“数组”。实际上,您是在告诉 MySQL 这样做:courseid IN('2,3'),它只是一个值,与所需的 courseid IN(2,3) 不同
  • @MonkeyZeus 感谢您的帮助,在使用$ids时请您帮忙解答一下@
  • @Rizier123 我已经命名了参数以及 IN 运算符,所以它不是完全重复的,或者请链接相关的。
  • courseid IN($ids) 会起作用,但它会让你对 SQL 注入持开放态度。您需要为每个 ID 添加一个参数,例如 courseid IN(:id1, :id2)

标签: php mysql database pdo in-operator


【解决方案1】:

这应该适合你:

正如在 cmets 中已经说过的,您需要为要绑定到 IN 子句中的每个值一个占位符。

这里我首先创建数组$ids,它只保存普通的ID,例如

[2, 3]

然后我还创建了数组$preparedIds,它将占位符保存为数组,稍后您将在准备好的语句中使用它。这个数组看起来像这样:

[":id2", ":id3"]

我还创建了一个名为$preparedValues 的数组,其中包含$preparedIds 作为键和$ids 作为值,然后您可以将其用于execute() 调用。数组看起来像这样:

[":id2" => 2, ":id3" => 3]

在这之后你就可以走了。在准备好的语句中我只是implode()$preparedIds 数组,所以 SQL 语句看起来像这样:

... IN(:id2,:id3) ...

然后您可以简单地execute() 您的查询。那里我只是 array_merge() 你的 $preparedValues 数组和其他占位符数组。

<?php

    $ids = array_map(function($item){
        return $item->id;
    }, $entitlementsVOs);

    $preparedIds = array_map(function($v){
        return ":id$v";
    }, $ids);

    $preparedValues = array_combine($preparedIds, $ids);


    $timestart = (!empty($_GET['start']) ? $_GET['start'] : NULL );
    $timeend = (!empty($_GET['end']) ? $_GET['end'] : NULL );


    $statement = $this->connection->prepare("SELECT name AS title, timestart AS start, timestart + timeduration AS end FROM event WHERE courseid IN(" . implode(",", $preparedIds) . ") AND timestart >= :timestart AND timestart + timeduration <= :timeend");
    $statement->setFetchMode(\PDO::FETCH_CLASS, get_class(new EventVO()));

    if($statement->execute(array_merge($preparedValues, ["timestart" => $timestart, "timeend" => $timeend]))) {
        return $statement->fetchAll();
    } else {
        return null;
    }

?>

另外我认为你想在你的查询周围加上一个 if 语句,因为如果 $timestart$timeend 的值为 NULL,你的查询将不会运行。

【讨论】:

    猜你喜欢
    • 2014-05-23
    • 1970-01-01
    • 2014-03-12
    • 2012-10-10
    • 2016-04-04
    • 1970-01-01
    • 1970-01-01
    • 2016-10-16
    • 1970-01-01
    相关资源
    最近更新 更多