Ich verwende PDO, um eine Anweisung mit einer IN
Klausel auszuführen , die ein Array für ihre Werte verwendet:
$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();
Der obige Code funktioniert einwandfrei, aber meine Frage ist, warum dies nicht funktioniert:
$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();
Dieser Code gibt das Element zurück, das my_value
dem ersten Element in $in_array
(1) entspricht, jedoch nicht den verbleibenden Elementen im Array (2 und 3).
Antworten:
PDO ist mit solchen Dingen nicht gut. Sie müssen eine Zeichenfolge mit Platzhaltern dynamisch erstellen und in die Abfrage einfügen, während Sie die Array-Werte auf die übliche Weise binden. Mit Positionsplatzhaltern wäre es so:
$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();
Falls die Abfrage andere Platzhalter enthält, können Sie den folgenden Ansatz verwenden (der Code stammt aus meinem PDO-Tutorial ):
Mit der
array_merge()
Funktion können Sie alle Variablen zu einem einzigen Array zusammenfügen und Ihre anderen Variablen in Form von Arrays in der Reihenfolge hinzufügen, in der sie in Ihrer Abfrage angezeigt werden:$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();
Wenn Sie benannte Platzhalter verwenden, ist der Code etwas komplexer, da Sie eine Folge der benannten Platzhalter erstellen müssen, z
:id0,:id1,:id2
. Der Code wäre also:// 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 .= "$key,"; $in_params[$key] = $item; // collecting values into a key-value array } $in = rtrim($in,","); // :id0,:id1,:id2 $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();
Glücklicherweise müssen wir für die genannten Platzhalter nicht die strenge Reihenfolge einhalten, damit wir unsere Arrays in beliebiger Reihenfolge zusammenführen können.
quelle
-1
nach dem istcount()
?$in = implode(',', array_fill(0, count($in_array), '?'));
- Vermeidung von seltsamen Off-by-$in = rtrim(str_repeat('?,', count($array)), ',');
Die Variablensubstitution in PDO-vorbereiteten Anweisungen unterstützt keine Arrays. Es ist eins für eins.
Sie können dieses Problem umgehen, indem Sie die Anzahl der benötigten Platzhalter basierend auf der Länge des Arrays generieren.
$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)) { // ... }
quelle
count ($variables) - 1) . '?';
Warum nicht einfachcount($variable)
Da PDO keine gute Lösung zu bieten scheint, können Sie auch DBAL verwenden, das hauptsächlich der API von PDO folgt, aber auch einige nützliche Funktionen hinzufügt: http://docs.doctrine-project.org/projects/doctrine-dbal/ de / latest / reference / data-retrieval-and-manipulation.html # Liste der Parameterkonvertierung
$stmt = $conn->executeQuery('SELECT * FROM articles WHERE id IN (?)', array(array(1, 2, 3, 4, 5, 6)), array(\Doctrine\DBAL\Connection::PARAM_INT_ARRAY) );
Es gibt wahrscheinlich einige andere Pakete, die die Komplexität nicht erhöhen und die Interaktion mit der Datenbank nicht verdecken (wie es die meisten ORM tun), aber gleichzeitig kleine typische Aufgaben etwas vereinfachen.
quelle
Eine alternative Version von PHP Delusions (@ your-common-sense) mit Verschlüssen:
$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();
quelle
Soweit ich weiß, wird PDO den Inhalt von $ in_values als ein einzelnes Element behandeln und dies entsprechend tun. PDO sieht 1,2,3 als einzelne Zeichenfolge, sodass die Abfrage ungefähr so aussieht
SELECT * FROM Tabelle WHERE my_value IN ("1,2,3")
Sie können denken, dass das Ändern der Implode in Anführungszeichen und Kommas das Problem behebt, aber nicht. PDO sieht die Anführungszeichen und ändert, wie die Zeichenfolge in Anführungszeichen gesetzt wird.
Ich habe keine Erklärung, warum Ihre Abfrage mit dem ersten Wert übereinstimmt.
quelle
Ich bin gerade auf dieses Problem gestoßen und habe einen kleinen Wrapper codiert. Es ist nicht der schönste oder beste Code, da bin ich mir sicher, aber es könnte jemandem helfen, also hier ist es:
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; }
ZB diese weitergeben:
SELECT * FROM users WHERE userId IN (:ids) array(1) { ["ids"]=> array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } }
Wird:
SELECT * FROM users WHERE userId IN (:ids1, :ids2, :ids3) array(3) { [":ids1"]=> int(1) [":ids2"]=> int(2) [":ids3"]=> int(3) }
Es ist nicht kugelsicher, aber als einziger Entwickler für meine Bedürfnisse macht es den Job trotzdem gut.
quelle
Hier ist eine Lösung für unbenannte Platzhalter (?). Wenn Sie $ sql mit einem Fragezeichen wie "A =? AND B IN (?)" Und $ args übergeben, wobei einige der Elemente Arrays wie [1, [1,2,3]] sind, wird eine SQL-Zeichenfolge mit der entsprechenden Nummer zurückgegeben von Platzhaltern - "A =? UND B IN (?,?,?)" . Der Parameter $ args benötigt nur, um herauszufinden, welches Element ein Array ist und wie viele Platzhalter es benötigt. Sie können die kleine PDO-Erweiterungsklasse mit dieser Methode finden, mit der Ihre Abfrage ausgeführt wird: 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; }
quelle
Hier ist mein vollständiger Code, entschuldige meine dumme Codierung, schlechte Struktur und Kommentarzeilen. Vielleicht hat jemand meinen blöden Code neu codiert :)
Senden an die Klasse:
$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) ;
Klasse bekommen diese 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)
Klasse konvertieren diese SQL in diese.
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)
Klassenbindungsparameter:
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')
Code:
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; } }
}}
quelle