提问人:iRector 提问时间:2/8/2013 最后编辑:danronmooniRector 更新时间:3/22/2022 访问量:97679
PHP - 将 PDO 与 IN 子句数组一起使用 [duplicate]
PHP - Using PDO with IN clause array [duplicate]
问:
我正在使用 PDO 执行一个带有子句的语句,该子句使用数组作为其值:IN
$in_array = array(1, 2, 3);
$in_values = implode(',', $in_array);
$my_result = $wbdb->prepare("SELECT * FROM my_table WHERE my_value IN (".$in_values.")");
$my_result->execute();
$my_results = $my_result->fetchAll();
上面的代码工作得很好,但我的问题是为什么这不能:
$in_array = array(1, 2, 3);
$in_values = implode(',', $in_array);
$my_result = $wbdb->prepare("SELECT * FROM my_table WHERE my_value IN (:in_values)");
$my_result->execute(array(':in_values' => $in_values));
$my_results = $my_result->fetchAll();
此代码将返回与 (1) 中的第一个项目相等的项目,但不返回数组(2 和 3)中的其余项目。my_value
$in_array
答:
PDO 预准备语句中的变量替换不支持数组。这是一对一的。
您可以通过根据数组的长度生成所需的占位符数量来解决该问题。
$variables = array ('1', '2', '3');
$placeholders = str_repeat ('?, ', count ($variables) - 1) . '?';
$query = $pdo -> prepare ("SELECT * FROM table WHERE column IN($placeholders)");
if ($query -> execute ($variables)) {
// ...
}
评论
count ($variables) - 1) . '?';
count($variable)
implode(', ', array_fill(0, count($variables), '?'))
PDO对这样的事情并不擅长。您需要动态创建一个带有占位符的字符串,并将其插入到查询中,同时以通常的方式绑定数组值。对于位置占位符,它将是这样的:
$in = str_repeat('?,', count($in_array) - 1) . '?';
$sql = "SELECT * FROM my_table WHERE my_value IN ($in)";
$stm = $db->prepare($sql);
$stm->execute($in_array);
$data = $stm->fetchAll();
如果查询中有其他占位符,可以使用以下方法(代码取自我的 PDO 教程):
您可以使用函数将所有变量联接到一个数组中,以数组的形式添加其他变量,按照它们在查询中出现的顺序:array_merge()
$arr = [1,2,3];
$in = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM table WHERE foo=? AND column IN ($in) AND bar=? AND baz=?";
$stm = $db->prepare($sql);
$params = array_merge([$foo], $arr, [$bar, $baz]);
$stm->execute($params);
$data = $stm->fetchAll();
如果您使用的是命名占位符,则代码会稍微复杂一些,因为您必须创建命名占位符的序列,例如.所以代码将是::id0,:id1,:id2
// other parameters that are going into query
$params = ["foo" => "foo", "bar" => "bar"];
$ids = [1,2,3];
$in = "";
$i = 0; // we are using an external counter
// because the actual array keys could be dangerous
foreach ($ids as $item)
{
$key = ":id".$i++;
$in .= ($in ? "," : "") . $key; // :id0,:id1,:id2
$in_params[$key] = $item; // collecting values into a key-value array
}
$sql = "SELECT * FROM table WHERE foo=:foo AND id IN ($in) AND bar=:bar";
$stm = $db->prepare($sql);
$stm->execute(array_merge($params,$in_params)); // just merge two arrays
$data = $stm->fetchAll();
幸运的是,对于命名的占位符,我们不必遵循严格的顺序,因此我们可以按任何顺序合并数组。
评论
-1
count()
$in = implode(',', array_fill(0, count($in_array), '?'));
$in = rtrim(str_repeat('?,', count($array)), ',');
据我了解,这是因为 PDO 会将 $in_values 内容视为单个项目,并相应地将其处理。PDO 会将 1,2,3 视为单个字符串,因此查询将如下所示
从表中选择 * 其中my_value IN (“1,2,3”)
您可能认为将内爆更改为使用引号和逗号会修复它,但事实并非如此。 PDO 将看到引号并更改它引用字符串的方式。
至于为什么您的查询与第一个值匹配,我没有解释。
我刚刚遇到了这个问题并编写了一个小包装器。我敢肯定,这不是最漂亮或最好的代码,但它可能会对某人有所帮助,所以这里是:
function runQuery(PDO $PDO, string $sql, array $params = [])
{
if (!count($params)) {
return $PDO->query($sql);
}
foreach ($params as $key => $values) {
if (is_array($values)) {
// get placeholder from array, e.g. ids => [7,12,3] would be ':ids'
$oldPlaceholder = ':'.$key;
$newPlaceholders = '';
$newParams = [];
// loop through array to create new placeholders & new named parameters
for($i = 1; $i <= count($values); $i++) {
// this gives us :ids1, :ids2, :ids3 etc
$newKey = $oldPlaceholder.$i;
$newPlaceholders .= $newKey.', ';
// this builds an associative array of the new named parameters
$newParams[$newKey] = $values[$i - 1];
}
//trim off the trailing comma and space
$newPlaceholders = rtrim($newPlaceholders, ', ');
// remove the old parameter
unset($params[$key]);
// and replace with the new ones
$params = array_merge($params, $newParams);
// amend the query
$sql = str_replace($oldPlaceholder, $newPlaceholders, $sql);
}
}
$statement = $PDO->prepare($sql);
$statement->execute($params);
return $statement;
}
例如,将这些传入:
SELECT * FROM users WHERE userId IN (:ids)
array(1) {
["ids"]=>
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
}
成为:
SELECT * FROM users WHERE userId IN (:ids1, :ids2, :ids3)
array(3) {
[":ids1"]=>
int(1)
[":ids2"]=>
int(2)
[":ids3"]=>
int(3)
}
它不是防弹的,但作为满足我需求的唯一开发人员,无论如何,到目前为止,它可以很好地完成工作。
由于 PDO 似乎没有提供一个好的解决方案,您不妨考虑使用 DBAL,它主要遵循 PDO 的 API,但也增加了一些有用的功能 http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html#list-of-parameters-conversion
$stmt = $conn->executeQuery('SELECT * FROM articles WHERE id IN (?)',
array(array(1, 2, 3, 4, 5, 6)),
array(\Doctrine\DBAL\Connection::PARAM_INT_ARRAY)
);
可能还有一些其他的包,它们不会增加复杂性,也不会掩盖与数据库的交互(就像大多数ORM所做的那样),但同时使小型典型任务变得更容易。
这是未命名占位符 (?) 的解决方案。如果你用问号通过$sql,比如“A=?和 B 在(?)“,$args某些元素是数组,如 [1, [1,2,3]],它将返回具有适当数量占位符的 SQL 字符串 - ”A=?和 B IN(?,?,?)”。 它只需要$args参数来查找哪个元素是数组以及它需要多少个占位符。 可以使用以下方法找到将运行查询的小型 PDO 扩展类:https://github.com/vicF/pdo/blob/master/src/PDO.php
public function replaceArrayPlaceholders($sql, $args)
{
$num = 0;
preg_match_all('/\?/', $sql, $matches, PREG_OFFSET_CAPTURE); // Captures positions of placeholders
//echo $matches[0][1][1];
$replacements = [];
foreach($args as $arg) {
if(is_array($arg)) {
$replacements[$matches[0][$num][1]] = implode(',',array_fill(0, count($arg), '?')); // Create placeholders string
}
$num++;
}
krsort($replacements);
foreach($replacements as $position => $placeholders) {
$sql = substr($sql, 0, $position).$placeholders.substr($sql, $position+1); // Replace single placeholder with multiple
}
return $sql;
}
使用闭包的 PHP 妄想(@your常识)的替代版本:
$filter = ["min_price" => "1.98"];
$editions = [1,2,10];
$editions = array_combine(
array_map(function($i){ return ':id'.$i; }, array_keys($editions)),
$editions
);
$in_placeholders = implode(',', array_keys($editions));
$sql = "SELECT * FROM books WHERE price >= :min_price AND edition IN ($in_placeholders)";
$stm = $pdo->prepare($sql);
$stm->execute(array_merge($filter,$editions));
$data = $stm->fetchAll();
评论
$editions
这是我的完整代码,对不起我愚蠢的编码、糟糕的结构和注释行。也许有人重新编码了我愚蠢的代码:)
发送到课堂:
$veri_sinifi = new DB_Connect;
$veri_sorgu_degerleri=array(
"main_user_id" => (int)$_SESSION['MM_UserId'],
"cari_grup_id" => [71,72,73],
"cari_grup_adi" => ['fatih','ahmet','ali']
);
$siteler =$veri_sinifi->query("Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id) and cari_grup_adi in (:cari_grup_adi)",$veri_sorgu_degerleri) ;
类获取此 SQL :
Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id) and cari_grup_adi in (:cari_grup_adi)
类将此 SQL 转换为此。
Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id0,:cari_grup_id1,:cari_grup_id2) and cari_grup_adi in (:cari_grup_adi0,:cari_grup_adi1,:cari_grup_adi2)
类绑定参数:
Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= 1 and cari_grup_id in (71,72,73) and cari_grup_adi in ('fatih','ahmet','ali')
法典:
class DB_Connect{
var $dbh;
function __construct(){
$host = "";
$db = "";
$user = "";
$password = "";
$this -> dbh = $this -> db_connect($host, $db, $user, $password);
}
public function getDBConnection(){
return $this -> dbh;
}
protected function db_connect($host, $db, $user, $password){
//var_dump($host, $db, $user, $password);exit();
try {
$dbh = new PDO("mysql:host=$host;dbname=$db", $user, $password);
}
catch(PDOException $err) {
echo "Error: ".$err->getMessage()."<br/>";
die();
}
return $dbh;
}
public function query($statement,$bind_params){
$keyword = substr(strtoupper($statement), 0, strpos($statement, " ")); // sql in en başındaki kelimeye bakıyor SELECT UPDATE vs gibi ordaki ilk boşluğa kadar olan kelimeyi alıyor.
//echo $keyword;
$dbh = $this->getDBConnection();
if($dbh){
try{
$sql = $statement;
/*GELEN PARAMETRELERE BAKIP İÇİNDE ARRAY VAR İSE SQL STATEMENT KISMINI ONA GÖRE DEĞİŞTİRİYORUZ.
Alttaki döngünün yaptığı işlem şu. Eğer alttaki gibi bir sorgu değerleri gönderilirse
$veri_sorgu_degerleri=array(
"main_user_id" => (int)$_SESSION['MM_UserId'],
"cari_grup_id" => [71,72,73],
"cari_grup_adi" => ['fatih','ahmet','ali']
);
burada main_user_id tek bir değer diğerleri sise array olarak gönderiliyor. Where IN sorgusu birden fazla değer alabileceği için bunları PDO kabul ederken string olarak kabul ediyor yani yukardaki 71,72,73 değerini tırnak içine tek değermiş gib '71,72,73' şeklinde alıyor yapılması gereken sorgunun değiştirilmesi. bu döngü ile
Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id) and cari_grup_adi in (:cari_grup_adi)
şeklindeki sorgu in kısımları değiştirilerek
Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id0,:cari_grup_id1,:cari_grup_id2) and cari_grup_adi in (:cari_grup_adi0,:cari_grup_adi1,:cari_grup_adi2)
halini alıyor bir sonraki foreach de ise yine benzer yapı ile arary olarak gelen değerler in için tek tek bind ediliyor, normal gelen değerler ise normal bind yapılıyor.
*/
foreach($bind_params as $paramkey => $param_value) {
//echo "Key=" . $paramkey ."-".($param_value)."-, Value=" . $param_value;
//echo "<br>";
//echo "is_numeric($param_value)>".is_numeric($param_value)."<br>";
//echo "is_string($param_value)>".is_string($param_value)."<br>";
//echo "is_array($param_value)>".is_array($param_value)."<br>";
$in_key="";
$in_parameters="";
if (is_array($param_value)) // Gelan parametre array ise yeniden yapılandır.
{
foreach ($param_value as $i => $item)
{
$in_key = ":$paramkey".$i;
//echo "<br>$in_key = ".$paramkey.".$i";
$in_parameters .= "$in_key,";
//echo "<br>$in_parameters = ".$in_key;
}
$in_parameters = rtrim($in_parameters,","); // :id0,:id1,:id2
//echo "<br>in_parameters>$in_parameters";
$sql = str_replace(":".$paramkey, $in_parameters,$sql);
//echo "<br>oluşan sql>".$sql."<br>";
}
}
$sql = $dbh->prepare($sql);
foreach($bind_params as $paramkey => $param_value) {
//echo "Key=" . $paramkey ."-".($param_value)."-, Value=" . $param_value;
//echo "<br>";
//echo "is_numeric($param_value)>".is_numeric($param_value)."<br>";
//echo "is_string($param_value)>".is_string($param_value)."<br>";
//echo "is_array($param_value)>".is_array($param_value)."<br>";
if (is_numeric($param_value)==1) // gelen veri numerik ise
{
$param_value = (int)$param_value;
$pdo_param_type = PDO::PARAM_INT;
}
elseif (is_string($param_value)==1)// gelen veri string ise
{$pdo_param_type = PDO::PARAM_STR; }
if (is_array($param_value)) // gelen veri array tipinde ise
{
foreach ($param_value as $i => $param_array_value) // bu döngünün açıklaması yukardaki döngü için yazılan açıklama içinde mevcut
{
$in_key = ":$paramkey".$i;
if (is_numeric($param_array_value)==1) // gelen veri numerik ise
{
$param_array_value = (int)$param_array_value;
$pdo_param_type = PDO::PARAM_INT;
}
elseif (is_string($param_array_value)==1)// gelen veri string ise
{$pdo_param_type = PDO::PARAM_STR; }
$sql->bindValue($in_key, $param_array_value, $pdo_param_type );
//echo "<br>oldu1";
//echo "<br> -$in_key-";
}
//$sql = str_replace(":".$paramkey, $in_parameters, $sql);
//echo "oluşan sql>".$sql."<br>";
}
else // array değilse aşağıdaki şekilde bind yap.
{
//echo "<br>oldu2";
$sql->bindValue(":$paramkey", $param_value, $pdo_param_type ); // bindparam foreach içinde kullanılmaz çünkü execute esnasında bind yaptığı için yani anlık olarak değerleri atamaddığı için for döngüsünde en sonda value değişkeni neyse tüm parametrelere onu atıyor, bu sebeple bindvalue kullanıyoruz.PDO::PARAM_INT
}
} // foreach
$exe = $sql->execute();
$sql->debugDumpParams();
//echo $exe;
}
catch(PDOException $err){
return $err->getMessage();
}
//BU KISMA AİT AÇIKLAMA AŞAĞIDAIR.
switch($keyword){ // sorgu çalıştıktan sonra sorgu sonucuna göre gerekli işlemler yapılıyor.
case "SELECT": // Sorgu select sorgusu ise
$result = array(); //sonuçları diziye aktaracak.
while($row = $sql->fetch(PDO::FETCH_ASSOC)){ // sonuç satırlarını tek tek okuyup
//echo $row;
$result[] = $row; // her bir satırı dizinin bir elemanına aktarıyor.bu değer diziden nasıl okunur açıklaması aşağıda
}
return $result; // sorgudan dönen diziyi doğrudan ana programa aktarıyor orada dizi olarak okunabilir.
break;
default:
return $exe;
break;
}
}
else{
return false;
}
}
}
我经常用 FIND_IN_SET 代替 IN,如下所示:
$in_array = array(1, 2, 3);
$in_values = implode(',', $in_array);
$my_result = $wbdb->prepare("SELECT * FROM my_table WHERE FIND_IN_SET(my_value, :in_values)");
$my_result->execute(array(':in_values' => $in_values));
$my_results = $my_result->fetchAll();
在性能方面,这不是最好的解决方案,但如果 $in_array 的可能数量选项有限,则通常不是问题。我经常将它用于 my_value 是枚举字段的状态。从来没有任何问题。
评论