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

donné un tableau d'ids $galleries = array(1,2,5) je veux avoir 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?

273
demandé sur Braiam 2009-05-25 23:32:26

18 réponses

attention! Cette réponse contient un sévère SQL injection la vulnérabilité. N'utilisez pas les exemples de code présentés ici, sans vous assurer que toute entrée externe est aseptisée.

$ids = join("','",$galleries);   
$sql = "SELECT * FROM galleries WHERE id IN ('$ids')";
278
répondu Flavius Stef 2017-08-22 17:24:00

utilisant une AOP: [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);

En Utilisant 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, il ressemble à ceci:

expr IN (value,...)

Nous pouvons construire une expression à placer à l'intérieur du () à partir de notre tableau. Notez qu'il doit y avoir au moins une valeur entre parenthèses ou MySQL retournera une erreur; cela revient à s'assurer que notre tableau d'entrées a au moins une valeur. Pour 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 ID s'appelle $ids :

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

$select = <<<SQL
    SELECT *
    FROM galleries
    WHERE id IN ($in);
SQL;

Étant donné un tableau de trois éléments $select :

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

Notez de nouveau qu'il y a un ? pour chaque élément du tableau d'entrée. Ensuite, nous utiliserons PDO ou MySQLi pour préparer et exécuter la requête comme indiqué ci-dessus.

utilisant le IN() opérateur à cordes

il est facile de changer entre les chaînes et les entiers à cause des paramètres liés. Pour AOP il y a aucun changement n'est requis; pour MySQLi, changez str_repeat('i', en str_repeat('s', si vous avez besoin de vérifier les chaînes.

[1]: j'ai omis de vérifier la brièveté d'une erreur. Vous devez vérifier les erreurs habituelles pour chaque méthode de base de données (ou définir votre pilote de base de données pour lancer des exceptions).

[2]: nécessite PHP 5.6 ou plus. De plus j'ai omis une vérification des erreurs pour brièveté.

281
répondu Levi Morrison 2015-05-15 20:58:40

ints:

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

cordes:

$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";
48
répondu user542568 2014-03-19 13:30:53

en supposant que vous nettoyez correctement vos entrées à l'avance...

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

alors ajustez simplement votre requête:

SELECT *
FROM galleries
WHERE id IN ( $matches ) 

indique des valeurs appropriées en fonction de votre ensemble de données.

26
répondu AvatarKava 2009-05-25 19:38:46

utiliser:

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

une simple boucle for each fonctionnera.

Flavius/AvatarKava façon est mieux, mais assurez-vous qu'aucun des valeurs de tableau contiennent des virgules.

10
répondu Matthew Flaschen 2017-05-23 12:10:32

comme réponse de Flavius Stef , vous pouvez utiliser intval() pour vous assurer que tous id sont des valeurs int:

$ids = join(',', array_map('intval', $galleries));  
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
8
répondu Van-Duyet Le 2017-05-23 11:55:07

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 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);
7
répondu artoodetoo 2017-11-08 09:28:26

Nous devons prendre soin de SQL injection des vulnérabilités et une vide . Je vais traiter les deux comme ci-dessous.

pour un tableau purement numérique, utilisez le type de conversion approprié intval ou floatval ou doubleval sur chaque élément. Pour les types de chaîne de caractères mysqli_real_escape_string() qui peuvent également être appliqués aux valeurs numériques si vous le souhaitez. MySQL permet des nombres aussi bien que des variantes de date comme la chaîne de caractères .

pour échapper de manière appropriée aux valeurs avant de passer à la requête, créer 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 très probablement déjà disponible pour vous dans votre application, ou peut-être que vous en avez déjà créé une.

assainir le réseau de cordes comme:

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

un tableau numérique peut être aseptisé par intval ou floatval ou doubleval au lieu de

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

puis finalement construire la condition de requête

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

ou

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

puisque le tableau peut aussi être vide parfois, comme $galleries = array(); il faut donc noter que IN () ne permet pas une liste vide. On peut aussi utiliser OR à la place, mais le problème demeure. Si la vérification ci-dessus, count($values) , est d'assurer la même.

et l'ajouter à la requête finale:

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

TIP : si vous voulez afficher tous les enregistrements (Pas de filtrage) dans le cas d'un tableau vide au lieu de cacher toutes les lignes, il suffit de remplacer 0 par 1 dans la partie fausse du ternaire.

6
répondu Izhar Aazmi 2015-10-18 20:05:31

plus sûr.

$galleries = array(1,2,5);
array_walk($galleries , 'intval');
$ids = implode(',', $galleries);
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
5
répondu Filipe 2015-04-13 15:41:31

nous pouvons utiliser cette clause" WHERE id IN " si nous filtrons correctement le tableau d'entrées. Quelque chose comme ceci:

$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);

i. e. maintenant, vous devez utiliser en toute sécurité $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";

5
répondu Supratim Roy 2015-04-15 17:15:52

Shrapnell's SafeMySQL bibliothèque pour PHP fournit type-hinted placeholders dans ses requêtes paramétrées, et comprend un couple de placeholders pratiques pour travailler avec des tableaux. Le paramètre ?a étend un tableau à une liste de chaînes échappées*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 effectue une coercition de type automatique, peu importe que SafeMySQL convertissez les ID ci - dessus en chaînes-vous obtiendrez toujours le bon résultat.

4
répondu Mark Amery 2015-05-17 19:03:03

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') ; , la suite des lignes de sortie de la table de textes 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 une table supplémentaire et en utilisant seulement SQL sans la nécessité d'utiliser PHP ou un autre langage de programmation.

4
répondu SERJOU 2015-10-18 19:46:24

parce que la question originale se rapporte à un tableau de nombres et que j'utilise un tableau de chaînes, Je n'ai pas pu faire fonctionner les exemples donnés.

j'ai trouvé que chaque chaîne devait être encapsulée dans des 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 voir la première fonction enveloppe chaque variable de tableau dans single quotes (\') et implose ensuite le tableau.

NOTE: $status n'a pas de guillemets simples dans la déclaration SQL.

il y a probablement une meilleure façon d'ajouter les citations, mais cela fonctionne.

2
répondu RJaus 2015-08-04 14:34:10

en plus d'utiliser la requête IN, vous avez deux options pour le faire car dans une requête IN il y a un risque de vulnérabilité D'injection SQL. Vous pouvez utiliser "151920920 le" bouclage pour obtenir les données exactes que vous voulez 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
répondu Gaurav Singh 2015-10-18 19:49:48

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();
2
répondu Ricardo Canelas 2016-08-19 10:45:30

ci-dessous est la méthode que j'ai utilisée, en utilisant AOP avec des espaces nommés pour d'autres données. Pour surmonter L'injection SQL, Je filtre le tableau pour n'accepter que les valeurs qui sont des entiers et rejeter tous 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);
0
répondu kojow7 2018-01-24 16:31:28

de manière Sûre sans 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 à array
  • array_map transformer toutes les valeurs du tableau en entiers
  • array_unique supprimer les valeurs répétées
  • array_filter supprimer les valeurs nulles
  • implode Adhérer à toutes les valeurs DANS la sélection
0
répondu Lito 2018-01-30 09:12:17

les méthodes de base pour prévenir L'injection de SQL sont:

  • Utiliser les requêtes préparées et les requêtes paramétrées
  • échapper aux caractères spéciaux dans votre variable dangereuse

L'utilisation de requêtes préparées et paramétrées est considérée comme une meilleure pratique, mais si vous choisissez la méthode des caractères échappés, vous pouvez essayer mon exemple ci-dessous.

vous pouvez générer les requêtes en utilisant array_map pour ajouter une seule citation à chacun des éléments du $galleries :

$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 . ");";

le $sql var généré sera:

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

Note: mysql_real_escape_string , comme décrit dans sa documentation ici , a été déprécié dans PHP 5.5.0, et il a été supprimé dans PHP 7.0.0. À la place, L'extension MySQLi ou PDO_MySQL doit être utilisé. Voir Aussi MySQL: choisir un guide D'API et la FAQ correspondante pour plus d'informations. Les solutions de rechange à cette fonction sont les suivantes:

  • mysqli_real_escape_string ()

  • AOP:: quote ()

-2
répondu David 2017-03-26 08:48:07