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?
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')";
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é.
ints:
$query = "SELECT * FROM `$table` WHERE `$column` IN(".implode(',',$array).")";
cordes:
$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";
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.
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.
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)";
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);
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.
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é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:
$galleryIds = implode(',', $galleries);
i. e. maintenant, vous devez utiliser en toute sécurité $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";
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.
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.
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.
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;
}
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();
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);
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
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 ()