在 Yii2 中使用 MySQL "IN" 语法绑定参数的正确方法?
Correct way to bind parameters using MySQL "IN" syntax in Yii2?
好的,我正在使用 Yii2
并且在使用 mysql 查询时我熟悉 preparing/binding 数据,例如:
$sql = $this->db->createCommand("UPDATE some_table SET something='foo' WHERE some_id=:some_id");
$sql->bindValue(':some_id', $some_id);
但是当值 可能 包含多个值时怎么办,例如使用 MySQL
语法时 IN
?
例如:
$sql = $this->db->createCommand("UPDATE some_table SET something='foo' WHERE some_id IN (:parents)");
$sql->bindValue(':parents', $parents);
据我所知,只有当 $parents
var 只有 one 值时,以上内容才会有效;但是如果它有多个值,例如 1,2,3
那么当您真正想要 '1','2','3'
或 1,2,3
.
时,您最终会得到类似 '1,2,3'
的结果
正确的做法是什么?
Yii2 的DB 功能是基于PDO
。
根据 bindValue
的手册,不支持 Array
类型的值。 (第三个参数 - data_type
)。
解决方案是在查询之前创建一个适合您的 IN
子句的字符串并将其绑定为字符串。
类似于:
$parents = "1,2,3";
/*
Or in case you already have an array of the desirable ids:
$parents_array = array(1,2,3);
$parents = implode(",",$parents_array);
*/
$sql = $this->db->createCommand("UPDATE some_table SET something='foo' WHERE some_id IN (:parents)");
$sql->bindValue(':parents', $parents);
编辑
似乎占位符被内爆数组替换为一个字符串值 '1,2,3'
而不是 '1','2','3'
(因为它是单个占位符)。
为了解决这个问题,我建议使用多个 ?
占位符。
因此,您可以使用 IN (?, ?, ?, ? ,....)
而不是 IN (:parents)
,并且由于我们已经有了排列好的数组 - 我们可以使用 count($array)
来了解我们需要放置多少占位符。
//$parents = array(1,2,3);
$placeholders = str_repeat('?,', count($parents) - 1). '?';
$sql = $this->db->createCommand("UPDATE some_table SET something='foo' WHERE some_id IN (".$placeholders.")");
foreach($parents as $i => $parent){
$sql->bindValue($i+1, $parent);
}
请注意bindValue第一个参数的传递值;
手册中提到的 $i+1
而不是 $i
的原因:
For a prepared statement using question mark placeholders, this will
be the 1-indexed position of the parameter.
有关更多信息和替代解决方案,请查看以下答案:
随心所欲
$parents = implode("','", $parrent_array);
$sql->bindValue(':parents', $parents);
应该可以解决问题。
我最后是这样的:
$parents_safe = '';
$parents_sep = explode(',', $parents);
foreach ($parents_sep as $parent) {
$parents_safe .= $this->db->quoteValue($parent) . ',';
}
$parents_safe = rtrim($parents_safe, ',');
其中 $this->db
是 Yii::$app->db
的实例。
您可以只使用 Yii 的 QueryBuilder 函数,一切都会自动处理。试试这个:
$params = [];
$sql = \Yii::$app->db->getQueryBuilder()->update('some_table', ['something' => 'foo'], ['some_id' => [1, 2, 3]], $params);
结果:
string(78) "UPDATE `some_table` SET `something`=:qp0 WHERE `some_id` IN (:qp1, :qp2, :qp3)"
array(4) { [":qp0"]=> string(3) "foo" [":qp1"]=> int(1) [":qp2"]=> int(2) [":qp3"]=> int(3) }
我找到了这个解决方案
$params = [];
$sql = \Yii::$app->db->getQueryBuilder()->buildCondition(['IN', 'some_id', $ids], $params);
//$sql = some_id NOT IN (:qp0, :qp1, :qp2)
//$params = [':qp0'=>1, ':qp1'=>2, ':qp2'=>3]
$this->db->createCommand("UPDATE some_table SET something='foo' WHERE $sql", $params);
如果数据受信任,这个解决方案非常有效:
$db = Yii::$app->db;
$ids = "'" . implode("','", $ids_array) . "'";
$result = $db->createCommand("
UPDATE some_table SET something='foo' WHERE some_id IN ($ids)
")->queryColumn();
如果有人需要 Yii1 的解决方案:
$db = \Yii::app()->db;
$inCondition = $db
->getCommandBuilder()
->createInCondition('some_table', 'some_id', $parents);
$command = $db
->createCommand("UPDATE some_table SET something = :foo WHERE $inCondition");
$command->bindValue(':foo', 'foo');
$command->execute();
好的,我正在使用 Yii2
并且在使用 mysql 查询时我熟悉 preparing/binding 数据,例如:
$sql = $this->db->createCommand("UPDATE some_table SET something='foo' WHERE some_id=:some_id");
$sql->bindValue(':some_id', $some_id);
但是当值 可能 包含多个值时怎么办,例如使用 MySQL
语法时 IN
?
例如:
$sql = $this->db->createCommand("UPDATE some_table SET something='foo' WHERE some_id IN (:parents)");
$sql->bindValue(':parents', $parents);
据我所知,只有当 $parents
var 只有 one 值时,以上内容才会有效;但是如果它有多个值,例如 1,2,3
那么当您真正想要 '1','2','3'
或 1,2,3
.
'1,2,3'
的结果
正确的做法是什么?
Yii2 的DB 功能是基于PDO
。
根据 bindValue
的手册,不支持 Array
类型的值。 (第三个参数 - data_type
)。
解决方案是在查询之前创建一个适合您的 IN
子句的字符串并将其绑定为字符串。
类似于:
$parents = "1,2,3";
/*
Or in case you already have an array of the desirable ids:
$parents_array = array(1,2,3);
$parents = implode(",",$parents_array);
*/
$sql = $this->db->createCommand("UPDATE some_table SET something='foo' WHERE some_id IN (:parents)");
$sql->bindValue(':parents', $parents);
编辑
似乎占位符被内爆数组替换为一个字符串值 '1,2,3'
而不是 '1','2','3'
(因为它是单个占位符)。
为了解决这个问题,我建议使用多个 ?
占位符。
因此,您可以使用 IN (?, ?, ?, ? ,....)
而不是 IN (:parents)
,并且由于我们已经有了排列好的数组 - 我们可以使用 count($array)
来了解我们需要放置多少占位符。
//$parents = array(1,2,3);
$placeholders = str_repeat('?,', count($parents) - 1). '?';
$sql = $this->db->createCommand("UPDATE some_table SET something='foo' WHERE some_id IN (".$placeholders.")");
foreach($parents as $i => $parent){
$sql->bindValue($i+1, $parent);
}
请注意bindValue第一个参数的传递值;
手册中提到的 $i+1
而不是 $i
的原因:
For a prepared statement using question mark placeholders, this will be the 1-indexed position of the parameter.
有关更多信息和替代解决方案,请查看以下答案:
随心所欲
$parents = implode("','", $parrent_array);
$sql->bindValue(':parents', $parents);
应该可以解决问题。
我最后是这样的:
$parents_safe = '';
$parents_sep = explode(',', $parents);
foreach ($parents_sep as $parent) {
$parents_safe .= $this->db->quoteValue($parent) . ',';
}
$parents_safe = rtrim($parents_safe, ',');
其中 $this->db
是 Yii::$app->db
的实例。
您可以只使用 Yii 的 QueryBuilder 函数,一切都会自动处理。试试这个:
$params = [];
$sql = \Yii::$app->db->getQueryBuilder()->update('some_table', ['something' => 'foo'], ['some_id' => [1, 2, 3]], $params);
结果:
string(78) "UPDATE `some_table` SET `something`=:qp0 WHERE `some_id` IN (:qp1, :qp2, :qp3)"
array(4) { [":qp0"]=> string(3) "foo" [":qp1"]=> int(1) [":qp2"]=> int(2) [":qp3"]=> int(3) }
我找到了这个解决方案
$params = [];
$sql = \Yii::$app->db->getQueryBuilder()->buildCondition(['IN', 'some_id', $ids], $params);
//$sql = some_id NOT IN (:qp0, :qp1, :qp2)
//$params = [':qp0'=>1, ':qp1'=>2, ':qp2'=>3]
$this->db->createCommand("UPDATE some_table SET something='foo' WHERE $sql", $params);
如果数据受信任,这个解决方案非常有效:
$db = Yii::$app->db;
$ids = "'" . implode("','", $ids_array) . "'";
$result = $db->createCommand("
UPDATE some_table SET something='foo' WHERE some_id IN ($ids)
")->queryColumn();
如果有人需要 Yii1 的解决方案:
$db = \Yii::app()->db;
$inCondition = $db
->getCommandBuilder()
->createInCondition('some_table', 'some_id', $parents);
$command = $db
->createCommand("UPDATE some_table SET something = :foo WHERE $inCondition");
$command->bindValue(':foo', 'foo');
$command->execute();