Étant donné un tableau d'identifiants $galleries = array(1,2,5)
je veux une requête SQL qui utilise les valeurs du tableau dans sa clause WHERE comme:
SELECT *
FROM galleries
WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */
Comment puis-je générer cette chaîne de requête à utiliser avec MySQL?
ATTENTION! Cette réponse contient une vulnérabilité grave injection SQL . N'utilisez PAS les exemples de code tels que présentés ici, sans vous assurer que toute entrée externe est désinfectée.
$ids = join("','",$galleries);
$sql = "SELECT * FROM galleries WHERE id IN ('$ids')";
en utilisant 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);
tilisation de 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();
Explication:
IN()
pour vérifier si une valeur existe dans une liste donnée.En général, cela ressemble à ceci:
expr IN (value,...)
Nous pouvons construire une expression à placer dans le ()
de notre tableau. Notez qu'il doit y avoir au moins une valeur entre parenthèses ou MySQL renverra une erreur; cela revient à s'assurer que notre tableau d'entrée a au moins une valeur. Pour vous aider à prévenir les attaques par injection SQL, générez d’abord un ?
pour chaque élément d’entrée afin de créer une requête paramétrée. Ici, je suppose que le tableau contenant vos identifiants s’appelle $ids
:
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
Étant donné un tableau d'entrée de trois éléments, $select
ressemblera à ceci:
SELECT *
FROM galleries
WHERE id IN (?, ?, ?)
Notez à nouveau qu'il existe un ?
pour chaque élément du tableau en entrée. Nous utiliserons ensuite PDO ou MySQLi pour préparer et exécuter la requête comme indiqué ci-dessus.
IN()
avec des chaînesIl est facile de changer entre les chaînes et les entiers en raison des paramètres liés. Pour le PDO, aucun changement n'est requis. pour MySQLi, changez str_repeat('i',
en str_repeat('s',
si vous devez vérifier les chaînes.
[1]: J'ai omis quelques erreurs de vérification de brièveté. Vous devez vérifier les erreurs habituelles pour chaque méthode de base de données (ou configurer votre pilote de base de données pour qu'il lève des exceptions).
[2]: Nécessite PHP 5.6 ou supérieur. Encore une fois, j'ai omis quelques erreurs de vérification de brièveté.
ints:
$query = "SELECT * FROM `$table` WHERE `$column` IN(".implode(',',$array).")";
cordes:
$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";
En supposant que vous désinfectez correctement vos entrées au préalable ...
$matches = implode(',', $galleries);
Puis ajustez simplement votre requête:
SELECT *
FROM galleries
WHERE id IN ( $matches )
Citez les valeurs de manière appropriée en fonction de votre jeu de données.
Utilisation:
select id from galleries where id in (1, 2, 5);
Une simple boucle for each
fonctionnera.
à la manière de Flavius / AvatarKava est préférable, mais assurez-vous qu'aucune des valeurs du tableau ne contient de virgule.
Comme réponse de Flavius Stef , vous pouvez utiliser intval()
pour vous assurer que toutes les id
sont des valeurs int:
$ids = join(',', array_map('intval', $galleries));
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
Pour MySQLi avec une fonction d'échappement:
$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)");
Pour les AOP avec déclaration préparée:
$qmarks = implode(',', array_fill(0, count($ids), '?'));
$sth = $dbh->prepare("SELECT * FROM galleries WHERE id IN ($qmarks)");
$sth->execute($ids);
Nous devrions nous occuper des injection SQL des vulnérabilités et d'une condition vide . Je vais gérer les deux comme ci-dessous.
Pour un tableau purement numérique, utilisez la conversion de type appropriée, à savoir intval
ou floatval
ou doubleval
sur chaque élément. Pour les types de chaîne mysqli_real_escape_string()
qui peut également être appliqué aux valeurs numériques si vous le souhaitez. MySQL autorise les nombres ainsi que les variantes de date sous forme de chaîne .
Pour échapper de manière appropriée aux valeurs avant de passer à la requête, créez une fonction similaire à:
function escape($string)
{
// Assuming $db is a link identifier returned by mysqli_connect() or mysqli_init()
return mysqli_real_escape_string($db, $string);
}
Une telle fonction serait probablement déjà disponible dans votre application, ou peut-être en avez-vous déjà créée une.
Désinfectez le tableau de chaînes comme suit:
$values = array_map('escape', $gallaries);
Un tableau numérique peut être nettoyé en utilisant intval
ou floatval
ou doubleval
à la place, comme il convient:
$values = array_map('intval', $gallaries);
Enfin, construisez la condition de requête
$where = count($values) ? "`id` = '" . implode("' OR `id` = '", $values) . "'" : 0;
ou
$where = count($values) ? "`id` IN ('" . implode("', '", $values) . "')" : 0;
Comme le tableau peut aussi être vide parfois, comme $galleries = array();
, nous devons donc noter que IN ()
n'autorise pas une liste vide. On peut aussi utiliser OR
à la place, mais le problème persiste. La vérification ci-dessus, count($values)
, vise à garantir la même chose.
Et ajoutez-le à la requête finale:
$query = 'SELECT * FROM `galleries` WHERE ' . $where;
TIP: Si vous souhaitez afficher tous les enregistrements (sans filtrage) dans le cas d'un tableau vide au lieu de masquer toutes les lignes, remplacez simplement 0 avec 1 dans la partie fausse du ternaire.
Plus sûr.
$galleries = array(1,2,5);
array_walk($galleries , 'intval');
$ids = implode(',', $galleries);
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
Nous pouvons utiliser cette clause "WHERE id IN" si nous filtrons correctement le tableau d'entrée. Quelque chose comme ça:
$galleries = array();
foreach ($_REQUEST['gallery_id'] as $key => $val) {
$galleries[$key] = filter_var($val, FILTER_SANITIZE_NUMBER_INT);
}
Comme dans l'exemple ci-dessous:
$galleryIds = implode(',', $galleries);
C'est à dire. maintenant vous devriez utiliser $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";
en toute sécurité
La bibliothèque SafeMySQL du colonel Shrapnel pour PHP fournit des espaces réservés avec indication de type dans ses requêtes paramétrées et inclut quelques espaces réservés pratiques pour l'utilisation de tableaux. L'espace réservé ?a
étend un tableau en une liste de chaînes * séparées par des virgules.
Par exemple:
$someArray = [1, 2, 5];
$galleries = $db->getAll("SELECT * FROM galleries WHERE id IN (?a)", $someArray);
* Notez que, puisque MySQL exécute une coercition automatique de types, il n’importe pas que SafeMySQL convertisse les identifiants ci-dessus en chaînes - vous obtiendrez toujours le résultat correct.
Vous pouvez avoir la table texts
(T_ID (int), T_TEXT (text))
et la table test
(id (int), var (varchar(255)))
Dans insert into test values (1, '1,2,3') ;
, les éléments suivants généreront des lignes de textes de table où 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
De cette façon, vous pouvez gérer une relation de base de données n2m simple sans table supplémentaire et en utilisant uniquement SQL sans avoir besoin d'utiliser PHP ou un autre langage de programmation.
Plus un exemple:
$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();
Outre l'utilisation de la requête IN, vous disposez de deux options: dans une requête IN, il existe un risque de vulnérabilité d'injection SQL. Vous pouvez utiliser en boucle pour obtenir les données exactes souhaitées ou vous pouvez utiliser la requête avec OU cas
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;
}
Voici la méthode que j'ai utilisée, en utilisant PDO avec des espaces réservés nommés pour d'autres données. Pour surmonter l'injection SQL, je filtre le tableau afin de n'accepter que les valeurs qui sont des entiers et de rejeter toutes les autres.
$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);
Parce que la question initiale concerne un tableau de nombres et que j'utilise un tableau de chaînes, je ne peux pas faire fonctionner les exemples donnés.
J'ai trouvé que chaque chaîne devait être encapsulée entre guillemets simples pour fonctionner avec la fonction IN()
.
Voici ma solution
foreach($status as $status_a) {
$status_sql[] = '\''.$status_a.'\'';
}
$status = implode(',',$status_sql);
$sql = mysql_query("SELECT * FROM table WHERE id IN ($status)");
Comme vous pouvez le voir, la première fonction encapsule chaque variable de tableau dans single quotes (\')
, puis implose le tableau.
REMARQUE: $status
n'a pas de guillemets simples dans l'instruction SQL.
Il existe probablement un moyen plus agréable d’ajouter les guillemets, mais cela fonctionne.
Manière sûre sans AOP:
$ids = array_filter(array_unique(array_map('intval', (array)$ids)));
if ($ids) {
$query = 'SELECT * FROM `galleries` WHERE `id` IN ('.implode(',', $ids).');';
}
(array)$ids
Conversion de la variable $ids
en tableauarray_map
Transforme toutes les valeurs d'un tableau en entiersarray_unique
Supprimer les valeurs répétéesarray_filter
Supprimer les valeurs nullesimplode
Joindre toutes les valeurs à la sélection IN