web-dev-qa-db-fra.com

Passer un tableau à une requête en utilisant une clause WHERE

É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?

298
Quinn

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')";
312
Flavius Stef

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:

Utilisez l'opérateur SQL 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.

Utilisation de l'opérateur IN() avec des chaînes

Il 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é.

295
Levi Morrison

ints:

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

cordes:

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

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.

28
AvatarKava

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.

11
Matthew Flaschen

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)";
8
Van-Duyet Le

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);
8
artoodetoo

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.

6
Izhar Aazmi

Plus sûr.

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

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:enter image description here

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

C'est à dire. maintenant vous devriez utiliser $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})"; en toute sécurité

5
Supratim Roy

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.

5
Mark Amery

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.

4
SERJOU

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();
3
Ricardo Canelas

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;
   }
2
Gaurav Singh

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);
2
kojow7

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.

2
RJaus

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 tableau
  • array_map Transforme toutes les valeurs d'un tableau en entiers
  • array_unique Supprimer les valeurs répétées
  • array_filter Supprimer les valeurs nulles
  • implode Joindre toutes les valeurs à la sélection IN
1
Lito