Übergeben eines Arrays an eine Abfrage mithilfe einer WHERE-Klausel

Bei einem Array von IDs $galleries = array(1,2,5) möchte ich eine SQL-Abfrage haben, die die Werte des Arrays in seiner WHERE-Klausel verwendet:

 SELECT * FROM galleries WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */ 

Wie kann ich diese Abfragezeichenfolge für die Verwendung mit MySQL generieren?

IN ACHT NEHMEN! Diese Antwort enthält eine schwerwiegende SQL- Sicherheitsanfälligkeit. Verwenden Sie die Codebeispiele NICHT wie hier dargestellt, ohne sicherzustellen, dass externe Eingaben bereinigt werden.

 $ids = join("','",$galleries); $sql = "SELECT * FROM galleries WHERE id IN ('$ids')"; 

Mit PDO: [1]

 $in = join(',', array_fill(0, count($ids), '?')); $select = < <prepare($select); $statement->execute($ids); 

MySQL verwenden [2]

 $in = join(',', array_fill(0, count($ids), '?')); $select = < <prepare($select); $statement->bind_param(str_repeat('i', count($ids)), ...$ids); $statement->execute(); $result = $statement->get_result(); 

Erläuterung:

Verwenden Sie den Operator SQL IN() , um zu prüfen, ob ein Wert in einer bestimmten Liste vorhanden ist.

Im Allgemeinen sieht es so aus:

 expr IN (value,...) 

Wir können einen Ausdruck erstellen, der innerhalb des () von unserem Array platziert wird. Beachten Sie, dass mindestens ein Wert innerhalb der Klammer vorhanden sein muss oder dass MySQL einen Fehler zurückgibt. Dies entspricht der Sicherstellung, dass unser Eingabearray mindestens einen Wert hat. Um zu verhindern, dass SQL-Injection-Angriffe auftreten, generieren Sie zunächst ein ? für jedes Eingabeelement, um eine parametrisierte Abfrage zu erstellen. Hier nehme ich an, dass das Array, das Ihre IDs enthält, $ids :

 $in = join(',', array_fill(0, count($ids), '?')); $select = < < 

Bei einem Eingabe-Array mit drei Elementen sieht $select wie folgt aus:

 SELECT * FROM galleries WHERE id IN (?, ?, ?) 

Beachten Sie, dass es ein ? für jedes Element im Eingabearray. Dann verwenden wir PDO oder MySQLi, um die Abfrage wie oben beschrieben vorzubereiten und auszuführen.

Den Operator IN() mit Zeichenfolgen verwenden

Aufgrund der gebundenen Parameter ist es leicht, zwischen Zeichenfolgen und Ganzzahlen zu wechseln. Für PDO ist keine Änderung erforderlich; für MySQLi ändern Sie str_repeat('i', zu str_repeat('s', wenn Sie Zeichenfolgen überprüfen müssen).

[1]: Ich habe einige Fehler aus Gründen der Kürze weggelassen. Sie müssen für jede databasemethode nach den üblichen Fehlern suchen (oder den DB-Treiber so einstellen, dass Ausnahmen ausgetriggers werden).

[2]: Benötigt PHP 5.6 oder höher. Wiederum habe ich einige Fehler aus Gründen der Kürze weggelassen.

ints:

 $query = "SELECT * FROM `$table` WHERE `$column` IN(".implode(',',$array).")"; 

Zeichenfolgen:

 $query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')"; 

Angenommen, Sie reinigen Ihre Eingaben vorher gründlich …

 $matches = implode(',', $galleries); 

Passen Sie dann einfach Ihre Anfrage an:

 SELECT * FROM galleries WHERE id IN ( $matches ) 

Zitieren Sie die Werte entsprechend Ihrem Datensatz.

Benutzen:

 select id from galleries where id in (1, 2, 5); 

Ein einfaches for each Schleife funktioniert.

Der Weg von Flavius ​​/ AvatarKava ist besser, aber stellen Sie sicher, dass keiner der Array-Werte Kommas enthält.

Als Antwort von Flavius ​​Stef können Sie intval() , um sicherzustellen, dass alle id int-Werte sind:

 $ids = join(',', array_map('intval', $galleries)); $sql = "SELECT * FROM galleries WHERE id IN ($ids)"; 

Für MySQLi mit einer Escape-function:

 $ids = array_map(function($a) use($mysqli) { return is_string($a) ? "'".$mysqli->real_escape_string($a)."'" : $a; }, $ids); $ids = join(',', $ids); $result = $mysqli->query("SELECT * FROM galleries WHERE id IN ($ids)"); 

Für PDO mit vorbereiteter Aussage:

 $qmarks = implode(',', array_fill(0, count($ids), '?')); $sth = $dbh->prepare("SELECT * FROM galleries WHERE id IN ($qmarks)"); $sth->execute($ids); 

Wir sollten auf SQL-Injection- Schwachstellen und eine leere Bedingung achten. Ich werde beide wie unten behandeln.

Verwenden Sie für ein reines numerisches Array die entsprechende Typkonvertierung viz intval oder floatval oder doubleval für jedes Element. Für String-Typen mysqli_real_escape_string() die bei Bedarf auch auf numerische Werte angewendet werden können. MySQL erlaubt sowohl Zahlen als auch Datumsvarianten als String .

Um die Werte vor der Übergabe an die Abfrage ordnungsgemäß zu umgehen, erstellen Sie eine ähnliche function wie:

 function escape($string) { // Assuming $db is a link identifier returned by mysqli_connect() or mysqli_init() return mysqli_real_escape_string($db, $string); } 

Eine solche function steht Ihnen wahrscheinlich in Ihrer Anwendung bereits zur Verfügung, oder Sie haben sie bereits erstellt.

Sanitize das String-Array wie:

 $values = array_map('escape', $gallaries); 

Ein numerisches Array kann stattdessen mit intval oder floatval oder doubleval als geeignet doubleval werden:

 $values = array_map('intval', $gallaries); 

Dann baue endlich die Abfragebedingung

 $where = count($values) ? "`id` = '" . implode("' OR `id` = '", $values) . "'" : 0; 

oder

 $where = count($values) ? "`id` IN ('" . implode("', '", $values) . "')" : 0; 

Da das Array manchmal auch leer sein kann, wie $galleries = array(); Wir sollten daher beachten, dass IN () keine leere Liste erlaubt. Man kann stattdessen auch OR verwenden, aber das Problem bleibt bestehen. Also, die obige Überprüfung, count($values) , ist dasselbe sicherzustellen.

Und fügen Sie es der letzten Abfrage hinzu:

 $query = 'SELECT * FROM `galleries` WHERE ' . $where; 

TIPP : Wenn Sie im Falle eines leeren Arrays alle Datensätze anzeigen möchten (keine Filterung), statt alle Zeilen zu verbergen, ersetzen Sie einfach 0 durch 1 im falschen Teil des Ternärs.

Sicherer.

 $galleries = array(1,2,5); array_walk($galleries , 'intval'); $ids = implode(',', $galleries); $sql = "SELECT * FROM galleries WHERE id IN ($ids)"; 

Wir können diese “WHERE ID IN” -Klausel verwenden, wenn wir das Eingangsarray richtig filtern. Etwas wie das:

 $galleries = array(); foreach ($_REQUEST['gallery_id'] as $key => $val) { $galleries[$key] = filter_var($val, FILTER_SANITIZE_NUMBER_INT); } 

Wie im folgenden Beispiel: Bildbeschreibung hier eingeben

 $galleryIds = implode(',', $galleries); 

Dh jetzt sollten Sie sicher $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";

Die SafeMySQL- Bibliothek von Col. Shrapnel für PHP bietet typisierte Platzhalter in ihren parametrisierten Abfragen und enthält einige praktische Platzhalter für die Arbeit mit Arrays. Der ?a Platzhalter erweitert ein Array zu einer durch Kommas getrennten Liste von Escapezeichenfolgen *.

Beispielsweise:

 $someArray = [1, 2, 5]; $galleries = $db->getAll("SELECT * FROM galleries WHERE id IN (?a)", $someArray); 

* Beachten Sie, dass, da MySQL eine automatische Typumwandlung durchführt, es nicht wichtig ist, dass SafeMySQL die obigen ids in Zeichenfolgen umwandelt – Sie erhalten immer noch das korrekte Ergebnis.

Sie haben möglicherweise Tabellentexte (T_ID (int), T_TEXT (text)) und Tabellentest (id (int), var (varchar(255)))

Bei insert into test values (1, '1,2,3') ; Im Folgenden werden Zeilen aus Tabellentexten ausgegeben, wobei T_ID IN (1,2,3) :

 SELECT * FROM `texts` WHERE (SELECT FIND_IN_SET( T_ID, ( SELECT var FROM test WHERE id =1 ) ) AS tm) >0 

Auf diese Weise können Sie eine einfache n2m-databasebeziehung ohne zusätzliche Tabelle verwalten und nur SQL verwenden, ohne PHP oder eine andere Programmiersprache verwenden zu müssen.

Da sich die ursprüngliche Frage auf ein Array von Zahlen bezieht und ich ein Array von Strings verwende, konnte ich die gegebenen Beispiele nicht ausführen.

Ich fand heraus, dass jeder String in einfache Anführungszeichen eingeschlossen werden musste, um mit der function IN() .

Hier ist meine Lösung

 foreach($status as $status_a) { $status_sql[] = '\''.$status_a.'\''; } $status = implode(',',$status_sql); $sql = mysql_query("SELECT * FROM table WHERE id IN ($status)"); 

Wie Sie sehen können, umschließt die erste function jede Array-Variable in single quotes (\') und implodiert dann das Array.

HINWEIS: $status enthält keine einfachen Anführungszeichen in der SQL-statement.

Es gibt wahrscheinlich einen schöneren Weg, die Anführungszeichen hinzuzufügen, aber das funktioniert.

Neben der Verwendung der IN-Abfrage haben Sie zwei Möglichkeiten dazu, da in einer IN-Abfrage das Risiko einer SQL-Injection-Schwachstelle besteht. Sie können Schleifen verwenden , um genau die gewünschten Daten zu erhalten, oder Sie können die Abfrage mit OR case verwenden

 1. SELECT * FROM galleries WHERE id=1 or id=2 or id=5; 2. $ids = array(1, 2, 5); foreach ($ids as $id) { $data[] = SELECT * FROM galleries WHERE id= $id; } 

Mehr ein Beispiel:

 $galleryIds = [1, '2', 'Vitruvian Man']; $ids = array_filter($galleryIds, function($n){return (is_numeric($n));}); $ids = implode(', ', $ids); $sql = "SELECT * FROM galleries WHERE id IN ({$ids})"; // output: 'SELECT * FROM galleries WHERE id IN (1, 2)' $statement = $pdo->prepare($sql); $statement->execute(); 

Unten ist die Methode, die ich verwendet habe, PDO mit benannten Platzhaltern für andere Daten zu verwenden. Um SQL Injection zu überwinden, filtere ich das Array, um nur die Werte zu akzeptieren, die ganze Zahlen sind, und alle anderen abzulehnen.

 $owner_id = 123; $galleries = array(1,2,5,'abc'); $good_galleries = array_filter($chapter_arr, 'is_numeric'); $sql = "SELECT * FROM galleries WHERE owner=:OWNER_ID AND id IN ($good_galleries)"; $stmt = $dbh->prepare($sql); $stmt->execute(array( "OWNER_ID" => $owner_id, )); $data = $stmt->fetchAll(PDO::FETCH_ASSOC); 

Sicherer Weg ohne PDO:

 $ids = array_filter(array_unique(array_map('intval', (array)$ids))); if ($ids) { $query = 'SELECT * FROM `galleries` WHERE `id` IN ('.implode(',', $ids).');'; } 
  • (array)$ids Cast $ids Variable für Array
  • array_map Verwandle alle Array-Werte in Ganzzahlen
  • array_unique wiederholte Werte
  • array_filter Entferne Nullwerte
  • implode Verbinden Sie alle Werte mit der IN-Auswahl

Grundlegende Methoden zum Verhindern der SQL-Injektion sind:

  • Verwenden Sie vorbereitete statementen und parametrisierte Abfragen
  • Sonderzeichen in Ihrer unsicheren Variablen umgehen

Wenn Sie vorbereitete statementen und parametrisierte Abfragen verwenden, wird die Abfrage als die bessere Methode angesehen. Wenn Sie jedoch die Methode für ausgehende Zeichen auswählen, können Sie das folgende Beispiel ausprobieren.

Sie können die Abfragen generieren, indem Sie array_map , um jedem Element in den $galleries array_map ein einzelnes Zitat hinzuzufügen:

 $galleries = array(1,2,5); $galleries_str = implode(', ', array_map(function(&$item){ return "'" .mysql_real_escape_string($item) . "'"; }, $galleries)); $sql = "SELECT * FROM gallery WHERE id IN (" . $galleries_str . ");"; 

Die generierte $ sql var wird sein:

 SELECT * FROM gallery WHERE id IN ('1', '2', '5'); 

Hinweis: mysql_real_escape_string , wie in der Dokumentation beschrieben , war in PHP 5.5.0 veraltet und wurde in PHP 7.0.0 entfernt. Stattdessen sollte die Erweiterung MySQLi oder PDO_MySQL verwendet werden. Siehe auch MySQL: Wählen Sie einen API-Leitfaden und verwandte FAQ für weitere Informationen. Alternativen zu dieser function sind:

  • mysqli_real_escape_string ()

  • PDO :: Zitat ()