Puis-je lier un tableau à une condition IN ()?


562

Je suis curieux de savoir s'il est possible de lier un tableau de valeurs à un espace réservé à l'aide de PDO. Le cas d'utilisation ici tente de transmettre un tableau de valeurs à utiliser avec une IN()condition.

J'aimerais pouvoir faire quelque chose comme ça:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

Et demandez à PDO de lier et de citer toutes les valeurs du tableau.

En ce moment je fais:

<?php
$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
foreach($ids as &$val)
    $val=$db->quote($val); //iterate through array and quote
$in = implode(',',$ids); //create comma separated list
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$in.')'
);
$stmt->execute();
?>

Ce qui fait certainement le travail, mais je me demande simplement s'il manque une solution intégrée?


3
Un guide complet sur la liaison d'un tableau à une condition IN () , y compris le cas où vous avez d'autres espaces réservés dans la requête
Your Common Sense

La question a été clôturée en double de cette question . J'ai inversé le drapeau en double car cette question a 4 ans de plus, a 4 fois le nombre de vues, 3 fois le nombre de réponses et 12 fois le score. C'est clairement la cible supérieure.
miken32

Quiconque regarde cela en 2020: vous pouvez essayer github.com/morris/dop pour cela.
morris4

Réponses:


262

je pense que soulmerge a raison. vous devrez construire la chaîne de requête.

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids), '?'));

$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

correction: dan, tu avais raison. corrigé le code (ne l'a pas testé cependant)

edit: chris (commentaires) et quelqu'un suggèrent que la boucle foreach ...

(...)
// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();

... pourrait être redondant, donc la foreachboucle et le $stmt->executepourrait être remplacé par juste ...

<?php 
  (...)
  $stmt->execute($ids);
?>

(encore une fois, je ne l'ai pas testé)


7
C'est une solution intéressante, et bien que je la préfère à itérer sur les ids et appeler PDO :: quote (), je pense que l'index du '?' les espaces réservés vont être gâchés si d'autres espaces réservés se produisent d'abord ailleurs dans la requête, non?
Andru

5
oui, ce serait un problème. mais dans ce cas, vous pouvez créer des paramètres nommés au lieu de?.
stefs

9
La vieille question, mais mérite d'être notée, je crois, est que le $foreachet bindValue()n'est pas requis - exécutez simplement avec le tableau. Par exemple:$stmt->execute($ids);
Chris

2
La génération des espaces réservés doit se faire comme cecistr_repeat('?,', count($array) - 1). '?';
Xeoncross

8
Juste un conseil pour ceux qui ne le savent pas, vous ne pouvez pas mélanger les paramètres nommés et non nommés. Par conséquent, si vous utilisez des paramètres nommés dans votre requête, changez-les en? Et augmentez ensuite le décalage de votre index bindValue pour faire correspondre la position des IN avec où qu'ils se trouvent par rapport aux autres? params.
juste

169

Pour quelque chose de rapide:

//$db = new PDO(...);
//$ids = array(...);

$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);

7
Excellent, je n'avais pas pensé utiliser l'argument input_parameters de cette façon. Pour ceux dont les requêtes ont plus de paramètres que la liste IN, vous pouvez utiliser array_unshift et array_push pour ajouter les arguments nécessaires au début et à la fin du tableau. Aussi, je préfère $input_list = substr(str_repeat(',?', count($ids)), 1);
orca

7
Vous pouvez également essayer str_repeat('?,', count($ids) - 1) . '?'. Un appel de fonction en moins.
orca du

5
@erfling, ceci est une déclaration préparée, d'où viendra l'injection? Je serai plus qu'heureux d'apporter des corrections si vous pouvez le confirmer avec une preuve réelle de cela.
DonVaughn

5
@erfling, oui, c'est correct, et lier les paramètres est exactement ce que nous faisons dans cet exemple en envoyant executeun tableau d'identifiants
DonVaughn

5
Oh en effet. D'une certaine manière, vous avez manqué le fait que vous passiez le tableau. Cela semble en effet sûr et une bonne réponse. Mes excuses.
erfling

46

Est-il si important d'utiliser une INdéclaration? Essayez d'utiliser FIND_IN_SETop.

Par exemple, il y a une requête dans PDO comme ça

SELECT * FROM table WHERE FIND_IN_SET(id, :array)

Ensuite, il vous suffit de lier un tableau de valeurs, implosé avec une virgule, comme celui-ci

$ids_string = implode(',', $array_of_smth); // WITHOUT WHITESPACES BEFORE AND AFTER THE COMMA
$stmt->bindParam('array', $ids_string);

et c'est fait.

UPD: Comme certaines personnes l'ont souligné dans les commentaires de cette réponse, il y a certaines questions qui devraient être explicitées.

  1. FIND_IN_SETn'utilise pas d'index dans une table, et il n'est pas encore implémenté - voir cet enregistrement dans le suivi des bogues MYSQL . Merci à @BillKarwin pour l'avis.
  2. Vous ne pouvez pas utiliser une chaîne avec une virgule à l'intérieur comme valeur du tableau pour la recherche. Il est impossible d'analyser une telle chaîne de la bonne manière aprèsimplode utilisé le symbole virgule comme séparateur. Merci à @VaL pour la note.

In fine, si vous n'êtes pas fortement dépendant des index et n'utilisez pas de chaînes avec virgule pour la recherche, ma solution sera beaucoup plus facile, plus simple et plus rapide que les solutions listées ci-dessus.


25
IN () peut utiliser un index et compte comme un balayage de plage. FIND_IN_SET () ne peut pas utiliser d'index.
Bill Karwin du

1
C'est un point. Je ne le savais pas. Mais de toute façon, il n'y a aucune exigence de performance dans la question. Pour les tables pas si grandes, c'est beaucoup mieux et plus propre qu'une classe séparée pour générer une requête avec différents nombres d'espaces réservés.
Tim Tonkonogov

11
Oui, mais qui a une table pas si grande de nos jours? ;-)
Bill Karwin

3
Un autre problème avec cette approche est que s'il y a une chaîne avec une virgule à l'intérieur? Par exemple, ... FIND_IN_SET(description,'simple,search')cela fonctionnera, mais FIND_IN_SET(description,'first value,text, with coma inside')échouera. Ainsi, la fonction recherchera "first value", "text", "with coma inside" au lieu de celle souhaitée"first value", "text, with coma inside"
VaL

33

Comme je fais beaucoup de requêtes dynamiques, c'est une fonction d'assistance super simple que j'ai faite.

public static function bindParamArray($prefix, $values, &$bindArray)
{
    $str = "";
    foreach($values as $index => $value){
        $str .= ":".$prefix.$index.",";
        $bindArray[$prefix.$index] = $value;
    }
    return rtrim($str,",");     
}

Utilisez-le comme ceci:

$bindString = helper::bindParamArray("id", $_GET['ids'], $bindArray);
$userConditions .= " AND users.id IN($bindString)";

Renvoie une chaîne :id1,:id2,:id3et met également à jour vos $bindArrayliaisons dont vous aurez besoin au moment d'exécuter votre requête. Facile!


6
C'est une bien meilleure solution, car elle ne rompt pas la règle de liaison des paramètres. C'est beaucoup plus sûr que d'avoir sql en ligne comme proposé par d'autres ici.
Dimitar Darazhanski

1
Génialité. Élégant. Parfait.
Ricalsin

17

La solution d'EvilRygy n'a pas fonctionné pour moi. Dans Postgres, vous pouvez faire une autre solution:


$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (string_to_array(:an_array, ','))'
);
$stmt->bindParam(':an_array', implode(',', $ids));
$stmt->execute();

Cela ne fonctionne pas: ERROR: operator does not exist: integer = text. Vous devez au moins ajouter un casting explicite.
collimarco

17

très propre pour postgres utilise le postgres-array ("{}"):

$ids = array(1,4,7,9,45);
$param = "{".implode(', ',$ids)."}";
$cmd = $db->prepare("SELECT * FROM table WHERE id = ANY (?)");
$result = $cmd->execute(array($param));

-il bloquer l'injection sql?
Fábio Zangirolami

1
@ FábioZangirolami c'est AOP, alors oui.
ESCOBAR

OUI, AOP! après 4 ans. Votre réponse a été très bien pour moi, très simple et efficace. Merci!!!
Fábio Zangirolami

14

Voici ma solution:

$total_items = count($array_of_items);
$question_marks = array_fill(0, $total_items, '?');
$sql = 'SELECT * FROM foo WHERE bar IN (' . implode(',', $question_marks ). ')';

$stmt = $dbh->prepare($sql);
$stmt->execute(array_values($array_of_items));

Notez l'utilisation de array_values. Cela peut résoudre les problèmes de commande clés.

Je fusionnais des tableaux d'ID, puis supprimais les éléments en double. J'avais quelque chose comme:

$ids = array(0 => 23, 1 => 47, 3 => 17);

Et cela échouait.


C'était une excellente solution. Contrairement à la création de la chaîne de requête, cela utilise correctement la liaison. Je recommande fortement celui-ci.
Lindylead

Remarque: À l'aide de votre solution, vous pouvez ajouter des éléments à l'avant ou à l'arrière du tableau, afin d'inclure d'autres liaisons. $original_array Ajouter des éléments avant le tableau d'origine: array_unshift($original_array, new_unrelated_item); Ajouter des éléments après le tableau d'origine: array_push($original_array, new_unrelated_item); Lorsque les valeurs sont liées, les éléments new_unrelated seront placés aux bons emplacements. Cela vous permet de mélanger des éléments matriciels et non matriciels. `
Lindylead

12

J'ai étendu PDO pour faire quelque chose de similaire à ce que suggère stefs, et cela a été plus facile pour moi à long terme:

class Array_Capable_PDO extends PDO {
    /**
     * Both prepare a statement and bind array values to it
     * @param string $statement mysql query with colon-prefixed tokens
     * @param array $arrays associatve array with string tokens as keys and integer-indexed data arrays as values 
     * @param array $driver_options see php documention
     * @return PDOStatement with given array values already bound 
     */
    public function prepare_with_arrays($statement, array $arrays, $driver_options = array()) {

        $replace_strings = array();
        $x = 0;
        foreach($arrays as $token => $data) {
            // just for testing...
            //// tokens should be legit
            //assert('is_string($token)');
            //assert('$token !== ""');
            //// a given token shouldn't appear more than once in the query
            //assert('substr_count($statement, $token) === 1');
            //// there should be an array of values for each token
            //assert('is_array($data)');
            //// empty data arrays aren't okay, they're a SQL syntax error
            //assert('count($data) > 0');

            // replace array tokens with a list of value tokens
            $replace_string_pieces = array();
            foreach($data as $y => $value) {
                //// the data arrays have to be integer-indexed
                //assert('is_int($y)');
                $replace_string_pieces[] = ":{$x}_{$y}";
            }
            $replace_strings[] = '('.implode(', ', $replace_string_pieces).')';
            $x++;
        }
        $statement = str_replace(array_keys($arrays), $replace_strings, $statement);
        $prepared_statement = $this->prepare($statement, $driver_options);

        // bind values to the value tokens
        $x = 0;
        foreach($arrays as $token => $data) {
            foreach($data as $y => $value) {
                $prepared_statement->bindValue(":{$x}_{$y}", $value);
            }
            $x++;
        }

        return $prepared_statement;
    }
}

Vous pouvez l'utiliser comme ceci:

$db_link = new Array_Capable_PDO($dsn, $username, $password);

$query = '
    SELECT     *
    FROM       test
    WHERE      field1 IN :array1
     OR        field2 IN :array2
     OR        field3 = :value
';

$pdo_query = $db_link->prepare_with_arrays(
    $query,
    array(
        ':array1' => array(1,2,3),
        ':array2' => array(7,8,9)
    )
);

$pdo_query->bindValue(':value', '10');

$pdo_query->execute();

1
J'ai abordé la première partie du commentaire de Mark, mais comme il l'a souligné, ce n'est toujours pas sûr si un jeton comme :arrayest dans une chaîne dans la requête.
Chris

4
Une note à tous les futurs lecteurs: cette solution ne doit jamais être utilisée. Les assertions ne sont pas destinées au code de production
Your Common Sense

1
YCS: merci pour le feedback, intéressé par votre avis sur l'approche en dehors de l'adéquation des assertions.
Chris

1
L'idée est à peu près la même, mais sans assertions et de manière plus simple et explicite - non pas comme exception pour un seul cas, mais comme moyen général de construire chaque requête. Chaque espace réservé est marqué avec son type. Cela rend la conjecture (comme if (is_array($data))une) inutile, mais rend le traitement des données plus précis.
Votre bon sens

1
Pour tous ceux qui lisent les commentaires: le problème mentionné par @Your Common Sense a été corrigé dans la révision 4 .
user2428118

11

En regardant PDO: Constantes prédéfinies, il n'y a pas de PDO :: PARAM_ARRAY dont vous auriez besoin car il est répertorié dans PDOStatement-> bindParam

bool PDOStatement :: bindParam (mixte $ paramètre, mixte & $ variable [, int $ data_type [, int $ longueur [, mixte $ driver_options]]])

Je ne pense donc pas que ce soit réalisable.


1
Je ne sais pas si ça marche. Je suppose que la chaîne implosée est citée.
soulmerge

2
Vous avez raison, les citations se sont échappées, ce qui n'a pas fonctionné. J'ai supprimé ce code.

11

Lorsque vous avez un autre paramètre, vous pouvez faire comme ceci:

$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$query = 'SELECT *
            FROM table
           WHERE X = :x
             AND id IN(';
$comma = '';
for($i=0; $i<count($ids); $i++){
  $query .= $comma.':p'.$i;       // :p0, :p1, ...
  $comma = ',';
}
$query .= ')';

$stmt = $db->prepare($query);
$stmt->bindValue(':x', 123);  // some value
for($i=0; $i<count($ids); $i++){
  $stmt->bindValue(':p'.$i, $ids[$i]);
}
$stmt->execute();

Merci pour la bonne réponse. C'était le seul de tous qui fonctionnait réellement pour moi. Cependant, j'ai vu 1 erreur. La variable $ rs devrait être $ stmt
Piet

11

Pour moi, la solution la plus sexy est de construire un tableau associatif dynamique et de l'utiliser

// A dirty array sent by user
$dirtyArray = ['Cecile', 'Gilles', 'Andre', 'Claude'];

// we construct an associative array like this
// [ ':name_0' => 'Cecile', ... , ':name_3' => 'Claude' ]
$params = array_combine(
    array_map(
        // construct param name according to array index
        function ($v) {return ":name_{$v}";},
        // get values of users
        array_keys($dirtyArray)
    ),
    $dirtyArray
);

// construct the query like `.. WHERE name IN ( :name_1, .. , :name_3 )`
$query = "SELECT * FROM user WHERE name IN( " . implode(",", array_keys($params)) . " )";
// here we go
$stmt  = $db->prepare($query);
$stmt->execute($params);

Difficile d'être certain sans l'essayer dans un scénario réel, mais ça va. + 1
Anant Singh --- Alive to Die

9

Je me rends également compte que ce fil est ancien, mais j'ai eu un problème unique où, lors de la conversion du pilote mysql qui sera bientôt déconseillé en pilote PDO, j'ai dû créer une fonction qui pourrait générer dynamiquement à la fois des paramètres normaux et des IN à partir du même tableau param. J'ai donc rapidement construit ceci:

/**
 * mysql::pdo_query('SELECT * FROM TBL_WHOOP WHERE type_of_whoop IN :param AND siz_of_whoop = :size', array(':param' => array(1,2,3), ':size' => 3))
 *
 * @param $query
 * @param $params
 */
function pdo_query($query, $params = array()){

    if(!$query)
        trigger_error('Could not query nothing');

    // Lets get our IN fields first
    $in_fields = array();
    foreach($params as $field => $value){
        if(is_array($value)){
            for($i=0,$size=sizeof($value);$i<$size;$i++)
                $in_array[] = $field.$i;

            $query = str_replace($field, "(".implode(',', $in_array).")", $query); // Lets replace the position in the query string with the full version
            $in_fields[$field] = $value; // Lets add this field to an array for use later
            unset($params[$field]); // Lets unset so we don't bind the param later down the line
        }
    }

    $query_obj = $this->pdo_link->prepare($query);
    $query_obj->setFetchMode(PDO::FETCH_ASSOC);

    // Now lets bind normal params.
    foreach($params as $field => $value) $query_obj->bindValue($field, $value);

    // Now lets bind the IN params
    foreach($in_fields as $field => $value){
        for($i=0,$size=sizeof($value);$i<$size;$i++)
            $query_obj->bindValue($field.$i, $value[$i]); // Both the named param index and this index are based off the array index which has not changed...hopefully
    }

    $query_obj->execute();

    if($query_obj->rowCount() <= 0)
        return null;

    return $query_obj;
}

Elle n'est pas encore testée mais la logique semble être là.

J'espère que cela aide quelqu'un dans la même position,

Edit: Après quelques tests, j'ai découvert:

  • AOP n'aime pas '.' en leur nom (ce qui est un peu stupide si vous me demandez)
  • bindParam est la mauvaise fonction, bindValue est la bonne fonction.

Code modifié en version de travail.


8

Une petite retouche sur le code de Schnalle

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids)-1, '?'));

$db   = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

//implode(',', array_fill(0, count($ids)-1), '?')); 
//'?' this should be inside the array_fill
//$stmt->bindValue(($k+1), $in); 
// instead of $in, it should be $id

1
J'ai dû supprimer -1 après le comptage ($ ids) pour que cela fonctionne pour moi ou il y aurait toujours un espace réservé manquant.
Marcel Burkhard

7

Quelle base de données utilisez-vous? Dans PostgreSQL, j'aime utiliser TOUT (tableau). Donc, pour réutiliser votre exemple:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

Malheureusement, ce n'est pas portable.

Sur d'autres bases de données, vous devrez créer votre propre magie, comme d'autres l'ont mentionné. Vous voudrez mettre cette logique dans une classe / fonction pour la rendre réutilisable dans votre programme bien sûr. Jetez un œil aux commentaires à la mysql_querypage sur PHP.NET pour plus de réflexions sur le sujet et des exemples de ce scénario.


4

Après avoir traversé le même problème, je suis allé vers une solution plus simple (bien que toujours pas aussi élégante PDO::PARAM_ARRAY ):

étant donné le tableau $ids = array(2, 4, 32):

$newparams = array();
foreach ($ids as $n => $val){ $newparams[] = ":id_$n"; }

try {
    $stmt = $conn->prepare("DELETE FROM $table WHERE ($table.id IN (" . implode(", ",$newparams). "))");
    foreach ($ids as $n => $val){
        $stmt->bindParam(":id_$n", intval($val), PDO::PARAM_INT);
    }
    $stmt->execute();

... etc

Donc, si vous utilisez un tableau de valeurs mixtes, vous aurez besoin de plus de code pour tester vos valeurs avant d'attribuer le type param:

// inside second foreach..

$valuevar = (is_float($val) ? floatval($val) : is_int($val) ? intval($val) :  is_string($val) ? strval($val) : $val );
$stmt->bindParam(":id_$n", $valuevar, (is_int($val) ? PDO::PARAM_INT :  is_string($val) ? PDO::PARAM_STR : NULL ));

Mais je n'ai pas testé celui-ci.


4

Comme je sais, il n'y a aucune possibilité de lier un tableau dans une déclaration PDO.

Mais il existe 2 solutions communes:

  1. Utilisez des espaces réservés positionnels (?,?,?,?) Ou des espaces réservés nommés (: id1,: id2,: id3)

    $ whereIn = implode (',', array_fill (0, count ($ ids), '?'));

  2. Tableau de devis plus tôt

    $ whereIn = array_map (array ($ db, 'quote'), $ ids);

Les deux options sont bonnes et sûres. Je préfère le second car il est plus court et je peux var_dump les paramètres si j'en ai besoin. En utilisant des espaces réservés, vous devez lier des valeurs et à la fin votre code SQL sera le même.

$sql = "SELECT * FROM table WHERE id IN ($whereIn)";

Et la dernière et importante pour moi est d'éviter l'erreur "le nombre de variables liées ne correspond pas au nombre de jetons"

Doctrine, c'est un excellent exemple d'utilisation des espaces réservés positionnels, uniquement parce qu'il a un contrôle interne sur les paramètres entrants.


4

Si la colonne ne peut contenir que des entiers, vous pouvez probablement le faire sans espaces réservés et simplement mettre les identifiants dans la requête directement. Il vous suffit de convertir toutes les valeurs du tableau en entiers. Comme ça:

$listOfIds = implode(',',array_map('intval', $ids));
$stmt = $db->prepare(
    "SELECT *
     FROM table
     WHERE id IN($listOfIds)"
);
$stmt->execute();

Cela ne devrait pas être vulnérable à une injection SQL.


3

voici ma solution. J'ai également étendu la classe PDO:

class Db extends PDO
{

    /**
     * SELECT ... WHERE fieldName IN (:paramName) workaround
     *
     * @param array  $array
     * @param string $prefix
     *
     * @return string
     */
    public function CreateArrayBindParamNames(array $array, $prefix = 'id_')
    {
        $newparams = [];
        foreach ($array as $n => $val)
        {
            $newparams[] = ":".$prefix.$n;
        }
        return implode(", ", $newparams);
    }

    /**
     * Bind every array element to the proper named parameter
     *
     * @param PDOStatement $stmt
     * @param array        $array
     * @param string       $prefix
     */
    public function BindArrayParam(PDOStatement &$stmt, array $array, $prefix = 'id_')
    {
        foreach($array as $n => $val)
        {
            $val = intval($val);
            $stmt -> bindParam(":".$prefix.$n, $val, PDO::PARAM_INT);
        }
    }
}

Voici un exemple d'utilisation du code ci-dessus:

$idList = [1, 2, 3, 4];
$stmt = $this -> db -> prepare("
  SELECT
    `Name`
  FROM
    `User`
  WHERE
    (`ID` IN (".$this -> db -> CreateArrayBindParamNames($idList)."))");
$this -> db -> BindArrayParam($stmt, $idList);
$stmt -> execute();
foreach($stmt as $row)
{
    echo $row['Name'];
}

Laissez-moi savoir ce que vous pensez


J'ai oublié de mentionner que cela est basé sur la réponse de user2188977, ci-dessous.
Lippai Zoltan

Je ne sais pas ce qu'est getOne (), il ne semble pas faire partie de PDO. Je ne l'ai vu que dans PEAR. Que fait-il exactement?
Lippai Zoltan

@YourCommonSense pouvez-vous publier votre fonction définie par l'utilisateur comme réponse?
nullité

Je suggérerais de passer le type de données à BindArrayParamdans le tableau associatif car vous semblez limiter cela aux entiers.
Ian Brindley

2

Il n'est pas possible d'utiliser un tableau comme celui-ci dans PDO.

Vous devez créer une chaîne avec un paramètre (ou utiliser?) Pour chaque valeur, par exemple:

:an_array_0, :an_array_1, :an_array_2, :an_array_3, :an_array_4, :an_array_5

Voici un exemple:

<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = join(
    ', ',
    array_map(
        function($index) {
            return ":an_array_$index";
        },
        array_keys($ids)
    )
);
$db = new PDO(
    'mysql:dbname=mydb;host=localhost',
    'user',
    'passwd'
);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$sqlAnArray.')'
);
foreach ($ids as $index => $id) {
    $stmt->bindValue("an_array_$index", $id);
}

Si vous souhaitez continuer à utiliser bindParam, vous pouvez le faire à la place:

foreach ($ids as $index => $id) {
    $stmt->bindParam("an_array_$index", $ids[$id]);
}

Si vous souhaitez utiliser des ?espaces réservés, vous pouvez le faire comme ceci:

<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = '?' . str_repeat(', ?', count($ids)-1);
$db = new PDO(
    'mysql:dbname=dbname;host=localhost',
    'user',
    'passwd'
);
$stmt = $db->prepare(
    'SELECT *
     FROM phone_number_lookup
     WHERE country_code IN('.$sqlAnArray.')'
);
$stmt->execute($ids);

Si vous ne savez pas si $ids est vide, vous devez le tester et gérer ce cas en conséquence (renvoyer un tableau vide, ou renvoyer un objet Null, ou lever une exception, ...).


0

Je suis allé un peu plus loin pour rapprocher la réponse de la question d'origine de l'utilisation des espaces réservés pour lier les paramètres.

Cette réponse devra faire deux boucles à travers le tableau à utiliser dans la requête. Mais cela résout le problème d'avoir d'autres espaces réservés de colonne pour des requêtes plus sélectives.

//builds placeholders to insert in IN()
foreach($array as $key=>$value) {
    $in_query = $in_query . ' :val_' . $key . ', ';
}

//gets rid of trailing comma and space
$in_query = substr($in_query, 0, -2);

$stmt = $db->prepare(
    "SELECT *
     WHERE id IN($in_query)";

//pind params for your placeholders.
foreach ($array as $key=>$value) {
    $stmt->bindParam(":val_" . $key, $array[$key])
}

$stmt->execute();

0

vous définissez d'abord le nombre de "?" dans la requête, puis par un "pour" envoyer des paramètres comme celui-ci:

require 'dbConnect.php';
$db=new dbConnect();
$array=[];
array_push($array,'value1');
array_push($array,'value2');
$query="SELECT * FROM sites WHERE kind IN (";

foreach ($array as $field){
    $query.="?,";
}
$query=substr($query,0,strlen($query)-1);
$query.=")";
$tbl=$db->connection->prepare($query);
for($i=1;$i<=count($array);$i++)
    $tbl->bindParam($i,$array[$i-1],PDO::PARAM_STR);
$tbl->execute();
$row=$tbl->fetchAll(PDO::FETCH_OBJ);
var_dump($row);
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.