带有命名占位符的 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");
我得到了我所期待的结果,所以我相信 implode
、IN
运算符或 $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,您的查询将不会 运行。
假设一个场景:$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");
我得到了我所期待的结果,所以我相信 implode
、IN
运算符或 $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,您的查询将不会 运行。