Ich bin gespannt, ob es möglich ist, ein Array von Werten mithilfe von PDO an einen Platzhalter zu binden. Der Anwendungsfall hier ist der Versuch, ein Array von Werten zur Verwendung mit einer IN()
Bedingung zu übergeben.
Ich möchte in der Lage sein, so etwas zu tun:
<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
'SELECT *
FROM table
WHERE id IN(:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>
Lassen Sie PDO alle Werte im Array binden und in Anführungszeichen setzen.
Im Moment mache ich:
<?php
$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
foreach($ids as &$val)
$val=$db->quote($val); //iterate through array and quote
$in = implode(',',$ids); //create comma separated list
$stmt = $db->prepare(
'SELECT *
FROM table
WHERE id IN('.$in.')'
);
$stmt->execute();
?>
Was macht den Job sicher, aber ich frage mich nur, ob es eine eingebaute Lösung gibt, die mir fehlt?
Antworten:
Ich denke, Soulmerge ist richtig. Sie müssen die Abfragezeichenfolge erstellen.
fix: dan, du hattest recht. Der Code wurde behoben (aber nicht getestet)
edit: sowohl chris (kommentare) als auch jemandesintrouble schlugen vor, dass die foreach-loop ...
... könnte redundant sein, so dass die
foreach
Schleife und die$stmt->execute
durch nur ...(wieder habe ich es nicht getestet)
quelle
$foreach
undbindValue()
nicht erforderlich ist - einfach mit dem Array ausführen. ZB:$stmt->execute($ids);
str_repeat('?,', count($array) - 1). '?';
Für etwas schnelles:
quelle
$input_list = substr(str_repeat(',?', count($ids)), 1);
str_repeat('?,', count($ids) - 1) . '?'
. Ein Funktionsaufruf weniger.execute
ein Array von IDsIst es so wichtig, eine
IN
Anweisung zu verwenden ? Versuchen Sie,FIND_IN_SET
op zu verwenden .Zum Beispiel gibt es in PDO eine solche Abfrage
Dann müssen Sie nur noch ein Array von Werten binden, die wie dieses mit Komma implodiert sind
und es ist geschafft.
UPD: Wie einige Leute in Kommentaren zu dieser Antwort betonten, gibt es einige Probleme, die explizit angegeben werden sollten.
FIND_IN_SET
verwendet keinen Index in einer Tabelle und ist noch nicht implementiert - siehe diesen Datensatz im MYSQL-Bug-Tracker . Vielen Dank an @BillKarwin für den Hinweis.implode
da Sie das Kommasymbol als Trennzeichen verwenden. Vielen Dank an @VaL für den Hinweis.Wenn Sie nicht stark von Indizes abhängig sind und keine Zeichenfolgen mit Komma für die Suche verwenden, ist meine Lösung viel einfacher, einfacher und schneller als die oben aufgeführten Lösungen.
quelle
... FIND_IN_SET(description,'simple,search')
wird funktionieren, aberFIND_IN_SET(description,'first value,text, with coma inside')
wird fehlschlagen. Also wird die Funktion"first value", "text", "with coma inside"
statt gewünscht suchen"first value", "text, with coma inside"
Da ich viele dynamische Abfragen mache, ist dies eine super einfache Hilfsfunktion, die ich gemacht habe.
Verwenden Sie es so:
Gibt eine Zeichenfolge zurück
:id1,:id2,:id3
und aktualisiert auch Ihre$bindArray
Bindungen, die Sie zum Ausführen Ihrer Abfrage benötigen. Einfach!quelle
Die Lösung von EvilRygy hat bei mir nicht funktioniert. In Postgres können Sie eine andere Problemumgehung durchführen:
quelle
ERROR: operator does not exist: integer = text
. Zumindest müssen Sie explizites Casting hinzufügen.Ein sehr sauberer Weg für Postgres ist die Verwendung des Postgres-Arrays ("{}"):
quelle
Hier ist meine Lösung:
Beachten Sie die Verwendung von array_values. Dies kann wichtige Bestellprobleme beheben.
Ich habe Arrays von IDs zusammengeführt und dann doppelte Elemente entfernt. Ich hatte so etwas wie:
Und das schlug fehl.
quelle
$original_array
Elemente vor dem ursprünglichen Array hinzufügen :array_unshift($original_array, new_unrelated_item);
Elemente nach dem ursprünglichen Arrayarray_push($original_array, new_unrelated_item);
hinzufügen : Wenn die Werte gebunden sind, werden die Elemente new_unrelated an den richtigen Stellen platziert. Auf diese Weise können Sie Array- und Nicht-Array-Elemente mischen. `Ich habe PDO erweitert, um etwas Ähnliches zu tun, wie es Stefs vorschlägt, und es war auf lange Sicht einfacher für mich:
Sie können es so verwenden:
quelle
:array
in einer Zeichenfolge in der Abfrage befindet.if (is_array($data))
eines) unnötig und macht die Datenverarbeitung viel genauer.Betrachtet man PDO: Vordefinierte Konstanten, gibt es kein PDO :: PARAM_ARRAY, das Sie benötigen würden, wie unter PDOStatement-> bindParam aufgeführt
Ich denke also nicht, dass es erreichbar ist.
quelle
Wenn Sie einen anderen Parameter haben, können Sie Folgendes tun:
quelle
Für mich besteht die sexier Lösung darin, ein dynamisches assoziatives Array zu erstellen und es zu verwenden
quelle
Mir ist auch klar, dass dieser Thread alt ist, aber ich hatte ein einzigartiges Problem, bei dem ich beim Konvertieren des bald veralteten MySQL-Treibers in den PDO-Treiber eine Funktion erstellen musste, die dynamisch sowohl normale Parameter als auch INs daraus erstellen konnte param Array. Also habe ich das schnell gebaut:
Es ist noch nicht getestet, aber die Logik scheint da zu sein.
Hoffe, es hilft jemandem in der gleichen Position,
Edit: Nach einigen Tests fand ich heraus:
Code auf Arbeitsversion bearbeitet.
quelle
Eine kleine Bearbeitung über den Code von Schnalle
quelle
Welche Datenbank verwenden Sie? In PostgreSQL verwende ich gerne ANY (Array). Um Ihr Beispiel wiederzuverwenden:
Leider ist dies ziemlich nicht tragbar.
In anderen Datenbanken müssen Sie Ihre eigene Magie erfinden, wie andere bereits erwähnt haben. Sie sollten diese Logik in eine Klasse / Funktion einfügen, um sie natürlich in Ihrem gesamten Programm wiederverwendbar zu machen. In den Kommentaren auf der
mysql_query
Seite zu PHP.NET finden Sie weitere Gedanken zum Thema und Beispiele für dieses Szenario.quelle
Nachdem ich das gleiche Problem durchgearbeitet hatte, ging ich zu einer einfacheren Lösung (obwohl immer noch nicht so elegant wie es
PDO::PARAM_ARRAY
wäre):gegeben das Array
$ids = array(2, 4, 32)
:... und so weiter
Wenn Sie also ein Array mit gemischten Werten verwenden, benötigen Sie mehr Code, um Ihre Werte zu testen, bevor Sie den Typ param zuweisen:
Aber ich habe diesen nicht getestet.
quelle
Wie ich weiß, gibt es keine Möglichkeit, ein Array in eine PDO-Anweisung zu binden.
Es gibt aber 2 gängige Lösungen:
Verwenden Sie Positionsplatzhalter (?,?,?,?) Oder benannte Platzhalter (: id1 ,: id2 ,: id3).
$ whereIn = implode (',', array_fill (0, count ($ ids), '?'));
Array früher zitieren
$ whereIn = array_map (array ($ db, 'quote'), $ ids);
Beide Optionen sind gut und sicher. Ich bevorzuge die zweite, weil sie kürzer ist und ich var_dump-Parameter verwenden kann, wenn ich sie brauche. Mit Platzhaltern müssen Sie Werte binden und am Ende ist Ihr SQL-Code derselbe.
Und das Letzte und Wichtige für mich ist die Vermeidung des Fehlers "Anzahl der gebundenen Variablen stimmt nicht mit Anzahl der Token überein"
Doctrine ist ein großartiges Beispiel für die Verwendung von Positionsplatzhaltern, nur weil es die interne Kontrolle über eingehende Parameter hat.
quelle
Wenn die Spalte nur Ganzzahlen enthalten kann, können Sie dies wahrscheinlich ohne Platzhalter tun und die IDs direkt in die Abfrage einfügen. Sie müssen nur alle Werte des Arrays in Ganzzahlen umwandeln. So was:
Dies sollte für keine SQL-Injection anfällig sein.
quelle
Hier ist meine Lösung. Ich habe auch die PDO-Klasse erweitert:
Hier ist ein Beispiel für die Verwendung des obigen Codes:
Lass mich wissen was du denkst
quelle
BindArrayParam
im assoziativen Array zu übergeben, da Sie dies anscheinend auf ganze Zahlen beschränken.Es ist nicht möglich, ein solches Array in PDO zu verwenden.
Sie müssen für jeden Wert eine Zeichenfolge mit einem Parameter erstellen (oder verwenden?), Zum Beispiel:
:an_array_0, :an_array_1, :an_array_2, :an_array_3, :an_array_4, :an_array_5
Hier ist ein Beispiel:
Wenn Sie weiterhin verwenden möchten
bindParam
, können Sie stattdessen Folgendes tun:Wenn Sie
?
Platzhalter verwenden möchten , können Sie dies folgendermaßen tun:Wenn Sie nicht wissen, ob
$ids
es leer ist, sollten Sie es testen und den Fall entsprechend behandeln (ein leeres Array zurückgeben oder ein Nullobjekt zurückgeben oder eine Ausnahme auslösen, ...).quelle
Ich habe es etwas weiter gebracht, um die Antwort näher an die ursprüngliche Frage zu bringen, Platzhalter zum Binden der Parameter zu verwenden.
Diese Antwort muss zwei Schleifen durch das Array führen, um in der Abfrage verwendet zu werden. Es löst jedoch das Problem, andere Spaltenplatzhalter für selektivere Abfragen zu haben.
quelle
Sie haben zuerst die Nummer "?" in Abfrage und dann durch ein "für" Sendeparameter wie folgt:
quelle