Kann ich ein Array an eine IN()-Bedingung in einer PDO-Abfrage binden?

Lesezeit: 8 Minuten

Benutzer-Avatar
Andru

Ich bin neugierig zu wissen, ob es möglich ist, ein Array von Werten mit PDO an einen Platzhalter zu binden. Der Anwendungsfall hier versucht, ein Array von Werten zur Verwendung mit an zu übergeben IN() Zustand.

Ich würde gerne 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();
?>

Und lassen Sie PDO alle Werte im Array binden und zitieren.

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 sicherlich den Job macht, aber ich frage mich nur, ob es eine eingebaute Lösung gibt, die ich vermisse?

  • Eine vollständige Anleitung zum Binden eines Arrays an eine IN()-Bedingungeinschließlich des Falls, wenn Sie andere Platzhalter in der Abfrage haben

    – Ihr gesunder Menschenverstand

    18. April 2017 um 14:10 Uhr


  • Die Frage wurde als Duplikat dieser Frage geschlossen. Ich habe das Duplikat-Flag umgekehrt, weil diese Frage 4 Jahre älter ist, 4-mal so viele Aufrufe, 3-mal so viele Antworten und 12-mal so viele Punkte hat. Es ist eindeutig das übergeordnete Ziel.

    – Mike32

    7. März 2020 um 1:58 Uhr


  • Wer sich das 2020 anschaut: Man könnte es versuchen github.com/morris/dop dafür.

    – Morris4

    9. Mai 2020 um 16:55 Uhr

Benutzer-Avatar
stefs

Sie müssen die Abfragezeichenfolge konstruieren.

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids), '?'));

$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

Sowohl Chris (Kommentare) als auch Someoneisintrouble schlugen vor, dass die Foreach-Schleife …

(...)
// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();

… könnte überflüssig sein, so die foreach Schleife und die $stmt->execute könnte einfach ersetzt werden durch …

<?php 
  (...)
  $stmt->execute($ids);

  • Das ist eine interessante Lösung, und obwohl ich es vorziehe, über die IDs zu iterieren und PDO::quote() aufzurufen, denke ich, dass der Index des ‘?’ Platzhalter werden durcheinander gebracht, wenn zuerst andere Platzhalter an anderer Stelle in der Abfrage vorkommen, richtig?

    – Andru

    28. Mai 2009 um 12:49 Uhr

  • ja das wäre ein problem. aber in diesem Fall könnten Sie benannte Parameter anstelle von ? erstellen.

    – Stefs

    28. Mai 2009 um 13:31 Uhr

  • Alte Frage, aber erwähnenswert, glaube ich, ist das die $foreach und bindValue() ist nicht erforderlich – führen Sie einfach mit dem Array aus. Z.B: $stmt->execute($ids);

    – Chris

    30. Mai 2012 um 3:47 Uhr

  • Das Generieren der Platzhalter sollte so erfolgen str_repeat('?,', count($array) - 1). '?';

    – Xeoncross

    24. November 2012 um 20:13 Uhr

  • Nur ein Tipp für diejenigen, die es nicht wissen, Sie können benannte und unbenannte Parameter nicht mischen. Wenn Sie benannte Parameter in Ihrer Abfrage verwenden, tauschen Sie sie daher gegen ? aus und erweitern Sie dann Ihren bindValue-Index-Offset, um die Position der IN ? mit der Stelle abzugleichen, an der sie sich relativ zu Ihrem anderen ? befinden. Parameter.

    – justinl

    22. Oktober 2014 um 1:25 Uhr

Benutzer-Avatar
uɥƃnɐʌuop

Für was schnelles:

//$db = new PDO(...);
//$ids = array(...);

$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);

  • Ausgezeichnet, ich hatte nicht daran gedacht, das input_parameters-Argument auf diese Weise zu verwenden. Für diejenigen, deren Abfragen mehr Parameter als die IN-Liste haben, können Sie array_unshift und array_push verwenden, um die erforderlichen Argumente am Anfang und am Ende des Arrays hinzuzufügen. Außerdem bevorzuge ich $input_list = substr(str_repeat(',?', count($ids)), 1);

    – Orca

    31. Mai 2012 um 2:34 Uhr


  • Du könntest es auch versuchen str_repeat('?,', count($ids) - 1) . '?'. Ein Funktionsaufruf weniger.

    – Orca

    1. Juni 2012 um 23:30 Uhr

  • @erfling, das ist eine vorbereitete Aussage, wo soll die Spritze herkommen? Ich werde mehr als glücklich sein, Korrekturen vorzunehmen, wenn Sie das mit einem tatsächlichen Beweis dafür untermauern können.

    – uɥƃnɐʌuop

    30. November 2015 um 18:06 Uhr

  • @erfling, ja, das ist richtig, und das Binden der Params ist genau das, was wir in diesem Beispiel tun, indem wir senden execute ein Array von IDs

    – uɥƃnɐʌuop

    1. Dezember 2015 um 18:18 Uhr

  • Ach ja. Irgendwie übersehen, dass Sie das Array passiert haben. Dies scheint in der Tat sicher und eine gute Antwort zu sein. Entschuldigen Sie.

    – Erfling

    1. Dezember 2015 um 23:28 Uhr

Benutzer-Avatar
Tim Tonkonogow

Ist es so wichtig zu verwenden IN Aussage? Versuchen zu benutzen FIND_IN_SET op.

Zum Beispiel gibt es eine solche Abfrage in PDO

SELECT * FROM table WHERE FIND_IN_SET(id, :array)

Dann müssen Sie nur ein Array von Werten binden, die mit Kommas implodiert sind, wie dieses hier

$ids_string = implode(',', $array_of_smth); // WITHOUT WHITESPACES BEFORE AND AFTER THE COMMA
$stmt->bindParam('array', $ids_string);

und fertig.

UPD: Wie einige Leute in Kommentaren zu dieser Antwort darauf hingewiesen haben, gibt es einige Probleme, die ausdrücklich erwähnt werden sollten.

  1. FIND_IN_SET verwendet keinen Index in einer Tabelle und ist noch nicht implementiert – siehe diesen Eintrag im MYSQL-Bugtracker. Danke an @BillKarwin für den Hinweis.
  2. Sie können keine Zeichenfolge mit Komma als Wert des Arrays für die Suche verwenden. Es ist unmöglich, eine solche Zeichenfolge danach richtig zu analysieren implode da Sie das Komma-Symbol als Trennzeichen verwenden. Danke an @VaL für den Hinweis.

Kurz gesagt, 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.

  • IN() kann einen Index verwenden und zählt als Bereichsscan. FIND_IN_SET() kann keinen Index verwenden.

    – Bill Karwin

    3. Oktober 2013 um 0:59 Uhr

  • Das ist ein Argument. Ich wusste das nicht. Aber wie auch immer, es gibt keine Anforderungen an die Leistung in der Frage. Für nicht so große Tabellen ist es viel besser und sauberer als eine separate Klasse zum Generieren von Abfragen mit einer unterschiedlichen Anzahl von Platzhaltern.

    – Tim Tonkonogow

    8. Oktober 2013 um 0:03 Uhr

  • Ja, aber wer hat heutzutage einen nicht so großen Tisch? 😉

    – Bill Karwin

    8. Oktober 2013 um 0:17 Uhr

  • Ein weiteres Problem bei diesem Ansatz ist, was passiert, wenn eine Zeichenfolge mit Komma darin enthalten ist? Zum Beispiel... FIND_IN_SET(description,'simple,search') wird funktionieren, aber FIND_IN_SET(description,'first value,text, with coma inside') wird versagen. Die Funktion wird also suchen "first value", "text", "with coma inside" statt erwünscht "first value", "text, with coma inside"

    – Val

    17. Februar 2015 um 15:41 Uhr


Da ich viele dynamische Abfragen mache, ist dies eine supereinfache Hilfsfunktion, die ich erstellt habe.

public static function bindParamArray($prefix, $values, &$bindArray)
{
    $str = "";
    foreach($values as $index => $value){
        $str .= ":".$prefix.$index.",";
        $bindArray[$prefix.$index] = $value;
    }
    return rtrim($str,",");     
}

Verwenden Sie es wie folgt:

$bindString = helper::bindParamArray("id", $_GET['ids'], $bindArray);
$userConditions .= " AND users.id IN($bindString)";

Gibt eine Zeichenfolge zurück :id1,:id2,:id3 und aktualisiert auch Ihre $bindArray von Bindungen, die Sie benötigen, wenn es an der Zeit ist, Ihre Abfrage auszuführen. Leicht!

Ein sehr sauberer Weg für Postgres ist die Verwendung des Postgres-Arrays (“{}”):

$ids = array(1,4,7,9,45);
$param = "{".implode(', ',$ids)."}";
$cmd = $db->prepare("SELECT * FROM table WHERE id = ANY (?)");
$result = $cmd->execute(array($param));

  • Es blockiert die SQL-Injektion?

    – Fábio Zangirolami

    22. Februar 2018 um 14:10 Uhr

  • @FábioZangirolami es ist PDO, also ja.

    – Maik

    23. Februar 2018 um 12:56 Uhr

  • Um es klar zu sagen, PDO garantiert keine Sicherheit. Die richtige Verwendung vorbereiteter Anweisungen bietet Schutz vor Injection-Angriffen. (vorbereitete Anweisungen in mysqli oder pdo)

    – mickmackusa

    9. August 2021 um 7:06 Uhr

Die Lösung von EvilRygy hat bei mir nicht funktioniert. In Postgres können Sie eine andere Problemumgehung durchführen:


$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (string_to_array(:an_array, ','))'
);
$stmt->bindParam(':an_array', implode(',', $ids));
$stmt->execute();

  • Es blockiert die SQL-Injektion?

    – Fábio Zangirolami

    22. Februar 2018 um 14:10 Uhr

  • @FábioZangirolami es ist PDO, also ja.

    – Maik

    23. Februar 2018 um 12:56 Uhr

  • Um es klar zu sagen, PDO garantiert keine Sicherheit. Die richtige Verwendung vorbereiteter Anweisungen bietet Schutz vor Injection-Angriffen. (vorbereitete Anweisungen in mysqli oder pdo)

    – mickmackusa

    9. August 2021 um 7:06 Uhr

Hier ist meine Lösung:

$total_items = count($array_of_items);
$question_marks = array_fill(0, $total_items, '?');
$sql="SELECT * FROM foo WHERE bar IN (" . implode(',', $question_marks ). ')';

$stmt = $dbh->prepare($sql);
$stmt->execute(array_values($array_of_items));

Beachten Sie die Verwendung von array_values. Dadurch können Probleme bei der Schlüsselbestellung behoben werden.

Ich habe Arrays von IDs zusammengeführt und dann doppelte Elemente entfernt. Ich hatte so etwas wie:

$ids = array(0 => 23, 1 => 47, 3 => 17);

Und das scheiterte.

  • Hinweis: Mit Ihrer Lösung können Sie Elemente zur Vorder- oder Rückseite des Arrays hinzufügen, sodass Sie andere Bindungen einschließen können. $original_array Fügen Sie Elemente vor dem ursprünglichen Array hinzu: array_unshift($original_array, new_unrelated_item); Fügen Sie Elemente nach dem ursprünglichen Array hinzu: array_push($original_array, new_unrelated_item); Wenn die Werte gebunden sind, werden die new_unrelated-Elemente an den richtigen Stellen platziert. Auf diese Weise können Sie Array- und Nicht-Array-Elemente mischen. `

    – Lindylead

    19. Januar 2020 um 19:14 Uhr


1005520cookie-checkKann ich ein Array an eine IN()-Bedingung in einer PDO-Abfrage binden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy