带有命名占位符的 PDO 准备语句 IN 子句不能按预期工作

PDO prepared statements IN clause with named placeholders doesn't work as expected

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

我相信从完整代码来看这一行有一些问题,因为当我回显 $ids 时,它 returns 2,3,但实际查询 returns如果只有一个id。 IE。它 returns 仅来自一门课​​程(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;
}

p.s。手动 运行 mysql workbench returns 我的两个记录(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->execute.

存在一些问题

编辑:

我读过这个骗局,但我不知道从哪里开始使用命名占位符来完成相同的任务。我的问题是,当我同时使用命名参数和 IN 运算符时,受骗者仅将 IN 运算符与位置占位符一起使用。

编辑 2

我读过第二个骗局,它没有关系,我的问题混合使用了 IN 和命名占位符,链接的问题没有解决这个问题,但是,我现在在答案中找到了解决方案.

这应该适合你:

正如评论中所说,您需要为要绑定到 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,您的查询将不会 运行。