Wenn ich ein Array von ids $galleries = array(1,2,5)
gegeben habe, möchte ich eine SQL-Abfrage haben, die die Werte des Arrays in der WHERE-Klausel wie folgt 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?
ACHTUNG! Diese Antwort enthält eine schwerwiegende SQL Injection Sicherheitslücke. Verwenden Sie die hier gezeigten Codebeispiele NICHT, ohne sicherzustellen, dass externe Eingänge bereinigt sind.
$ids = join("','",$galleries);
$sql = "SELECT * FROM galleries WHERE id IN ('$ids')";
Mit PDO:[1]
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
$statement = $pdo->prepare($select);
$statement->execute($ids);
nter Verwendung von MySQLi [2]
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
$statement = $mysqli->prepare($select);
$statement->bind_param(str_repeat('i', count($ids)), ...$ids);
$statement->execute();
$result = $statement->get_result();
Erläuterung:
IN()
, um zu überprüfen, ob ein Wert in einer bestimmten Liste vorhanden ist.Im Allgemeinen sieht es so aus:
expr IN (value,...)
Wir können aus unserem Array einen Ausdruck erstellen, der in den ()
eingefügt wird. Beachten Sie, dass mindestens ein Wert in der Klammer enthalten sein muss, da MySQL sonst einen Fehler zurückgibt. Dies bedeutet, dass sichergestellt wird, dass unser Eingabearray mindestens einen Wert hat. Generieren Sie zur Vermeidung von SQL-Injection-Angriffen zunächst für jedes Eingabeelement einen ?
, um eine parametrisierte Abfrage zu erstellen. Hier gehe ich davon aus, dass das Array, das Ihre IDs enthält, $ids
heißt:
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
Bei einem Eingabearray von drei Elementen sieht $select
folgendermaßen aus:
SELECT *
FROM galleries
WHERE id IN (?, ?, ?)
Beachten Sie erneut, dass es für jedes Element im Eingabearray einen ?
gibt. Dann verwenden wir PDO oder MySQLi, um die Abfrage wie oben beschrieben vorzubereiten und auszuführen.
IN()
mit ZeichenfolgenAufgrund der gebundenen Parameter ist es einfach, zwischen Zeichenfolgen und Ganzzahlen zu wechseln. Für PDO ist keine Änderung erforderlich. Ändern Sie für MySQLi str_repeat('i',
in str_repeat('s',
, wenn Sie Zeichenfolgen überprüfen müssen.
[1] Ich habe einige Fehler bei der Überprüfung der Kürze weggelassen. Sie müssen für jede Datenbankmethode nach den üblichen Fehlern suchen (oder Ihren DB-Treiber so einstellen, dass Ausnahmen ausgelöst werden).
[2]: Benötigt PHP 5.6 oder höher. Wieder habe ich einige Fehler weggelassen, um die Kürze zu überprüfen.
ints:
$query = "SELECT * FROM `$table` WHERE `$column` IN(".implode(',',$array).")";
streicher:
$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";
Vorausgesetzt, Sie bereinigen Ihre Eingaben ordnungsgemäß im Voraus ...
$matches = implode(',', $galleries);
Dann passen Sie einfach Ihre Anfrage an:
SELECT *
FROM galleries
WHERE id IN ( $matches )
Zitieren Sie die Werte entsprechend Ihrem Datensatz.
Verwenden:
select id from galleries where id in (1, 2, 5);
Eine einfache for each
-Schleife funktioniert.
Flavius / AvatarKavas Weg ist besser, aber stellen Sie sicher, dass keiner der Array-Werte Kommas enthält.
Als Flavius Stefs Antwort können Sie intval()
verwenden, 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-Funktion:
$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 gU mit vorbereiteter Anweisung:
$qmarks = implode(',', array_fill(0, count($ids), '?'));
$sth = $dbh->prepare("SELECT * FROM galleries WHERE id IN ($qmarks)");
$sth->execute($ids);
Wir sollten uns um SQL Injection Schwachstellen und einen leeren Zustand kümmern. Ich werde beides wie folgt behandeln.
Verwenden Sie für ein reines numerisches Array die entsprechende Typkonvertierung, nämlich intval
oder floatval
oder doubleval
, für jedes Element. Für Zeichenfolgentypen mysqli_real_escape_string()
, die auf Wunsch auch auf numerische Werte angewendet werden können. MySQL erlaubt sowohl Zahlen als auch Datumsvarianten als String .
Erstellen Sie eine Funktion ähnlich der folgenden, um die Werte vor der Übergabe an die Abfrage entsprechend zu umgehen:
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 Funktion steht Ihnen höchstwahrscheinlich bereits in Ihrer Anwendung zur Verfügung oder Sie haben bereits eine erstellt.
Bereinigen Sie das String-Array wie folgt:
$values = array_map('escape', $gallaries);
Ein numerisches Array kann durch Verwendung von intval
oder floatval
oder doubleval
bereinigt werden:
$values = array_map('intval', $gallaries);
Dann bauen Sie endlich die Abfragebedingung auf
$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();
, sollten wir daher beachten, dass IN ()
keine leere Liste zulässt. Man kann stattdessen auch OR
verwenden, aber das Problem bleibt bestehen. Die obige Überprüfung, count($values)
, soll dasselbe sicherstellen.
Und füge es der letzten Abfrage hinzu:
$query = 'SELECT * FROM `galleries` WHERE ' . $where;
TIP: Wenn Sie bei einem leeren Array alle Datensätze anzeigen möchten (keine Filterung), anstatt alle Zeilen auszublenden, ersetzen Sie einfach 0 mit 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 Eingabearray 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:
$galleryIds = implode(',', $galleries);
Das heißt jetzt solltest du sicher $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";
verwenden
Die SafeMySQL -Bibliothek von Col. Shrapnel für PHP enthält typbezogene Platzhalter in den parametrisierten Abfragen und einige praktische Platzhalter für die Arbeit mit Arrays. Der Platzhalter ?a
erweitert ein Array zu einer durch Kommas getrennten Liste mit maskierten Zeichenfolgen *.
Zum Beispiel:
$someArray = [1, 2, 5];
$galleries = $db->getAll("SELECT * FROM galleries WHERE id IN (?a)", $someArray);
* Beachten Sie, dass es keine Rolle spielt, dass SafeMySQL die oben genannten IDs in Zeichenfolgen konvertiert, da MySQL eine automatische Typumwandlung durchführt - Sie erhalten trotzdem das richtige Ergebnis.
Möglicherweise haben Sie die Tabelle texts
_(T_ID (int), T_TEXT (text))
_ und die Tabelle test
_(id (int), var (varchar(255)))
_
In insert into test values (1, '1,2,3') ;
werden die folgenden Zeilen aus Tabellentexten ausgegeben, in denen 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-Datenbankbeziehung ohne eine zusätzliche Tabelle verwalten und nur SQL verwenden, ohne PHP oder eine andere Programmiersprache verwenden zu müssen.
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();
Neben der Verwendung der IN-Abfrage haben Sie zwei Möglichkeiten, da in einer IN-Abfrage das Risiko einer SQL-Injection-Sicherheitsanfälligkeit besteht. Sie können looping verwenden, um die gewünschten Daten zu erhalten, oder Sie können die Abfrage mit ODER 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;
}
Unten ist die Methode, die ich verwendet habe, mit PDO mit benannten Platzhaltern für andere Daten. Um die SQL-Injection zu überwinden, filtere ich das Array, um nur die Werte zu akzeptieren, die ganze Zahlen sind, und lehne alle anderen ab.
$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);
Da sich die ursprüngliche Frage auf ein Array von Zahlen bezieht und ich ein Array von Zeichenfolgen verwende, können die angegebenen Beispiele nicht funktionieren.
Ich fand, dass jeder String in einfache Anführungszeichen eingeschlossen werden musste, um mit der Funktion IN()
zu arbeiten.
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, umschließt die erste Funktion jede Array-Variable mit single quotes (\')
und implodiert dann das Array.
HINWEIS: $status
enthält keine einfachen Anführungszeichen in der SQL-Anweisung.
Es gibt wahrscheinlich eine schönere Möglichkeit, die Anführungszeichen hinzuzufügen, aber das funktioniert.
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
Konvertiert die Variable $ids
in ein Arrayarray_map
Alle Array-Werte in ganze Zahlen umwandelnarray_unique
Wiederholte Werte entfernenarray_filter
Nullwerte entfernenimplode
Verbinde alle Werte mit der IN-Auswahl