带有命名占位符的 PDO 准备好的语句 IN 子句无法按预期工作 [重复]

Posted

技术标签:

【中文标题】带有命名占位符的 PDO 准备好的语句 IN 子句无法按预期工作 [重复]【英文标题】:PDO prepared statements IN clause with named placeholders doesn't work as expected [duplicate] 【发布时间】: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) 【参考方案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,你的查询将不会运行。

【讨论】:

以上是关于带有命名占位符的 PDO 准备好的语句 IN 子句无法按预期工作 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

使用带占位符的 LIKE 准备语句 [重复]

MySQL 语法错误 1064,带有 PDO UPDATE 语句(带有命名或未命名占位符)返回 WSOD

如何将 pdo 的准备好的语句用于 order by 和 limit 子句?

在 PDO 中的执行函数中将 NULL 值绑定到具有关联数组的命名占位符的问题

PHP - 使用带有 IN 子句数组的 PDO

PHP - 使用带有 IN 子句数组的 PDO