¿Puedo vincular una matriz a una condición IN ()?

562

Tengo curiosidad por saber si es posible vincular una matriz de valores a un marcador de posición utilizando PDO. El caso de uso aquí está intentando pasar una matriz de valores para usar con una IN()condición.

Me gustaría poder hacer algo como esto:

<?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();
?>

Y haga que PDO enlace y cite todos los valores en la matriz.

En este momento estoy haciendo:

<?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();
?>

¿Qué es lo que sin duda hace el trabajo, pero me pregunto si me falta una solución integrada?

Andru
fuente
3
Una guía completa sobre cómo vincular una matriz a una condición IN () , incluido el caso en que tiene otros marcadores de posición en la consulta
su sentido común el
La pregunta se cerró como un duplicado de esta pregunta . Invertí el indicador duplicado porque esta pregunta es 4 años mayor, tiene 4 veces las vistas, 3 veces el número de respuestas y 12 veces el puntaje. Es claramente el objetivo superior.
miken32
Cualquiera que esté viendo esto en 2020: puede probar github.com/morris/dop para eso.
morris4

Respuestas:

262

Creo que soulmerge tiene razón. Tendrás que construir la cadena de consulta.

<?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();
?>

Solución: Dan, tenías razón. reparó el código (aunque no lo probé)

editar: tanto Chris (comentarios) como alguien en problemas sugirieron que el foreach-loop ...

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

$stmt->execute();

... podría ser redundante, por lo que el foreachbucle y el $stmt->executepodría reemplazarse por solo ...

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

(de nuevo, no lo probé)

stefs
fuente
77
Esa es una solución interesante, y aunque prefiero iterar sobre los identificadores y llamar a PDO :: quote (), creo que el índice de '?' los marcadores de posición se estropearán si algún otro marcador de posición ocurre en otra parte de la consulta primero, ¿verdad?
Andru
55
Sí, eso sería un problema. pero en este caso, podría crear parámetros con nombre en lugar de?
stefs
99
Cuestión de edad, pero vale la pena destacar que creo, es que el $foreachy bindValue()no es necesario - sólo hay que ejecutar con la matriz. Por ejemplo:$stmt->execute($ids);
Chris
2
La generación de los marcadores de posición debe hacerse asístr_repeat('?,', count($array) - 1). '?';
Xeoncross
8
Solo un consejo para aquellos que no saben, no puede mezclar parámetros con nombre y sin nombre. Por lo tanto, si utiliza parámetros con nombre en su consulta, cámbielos a? Y luego aumente el desplazamiento del índice bindValue para que coincida con la posición de los IN? params
justinl
169

Por algo rápido:

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

$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);
DonVaughn
fuente
77
Excelente, no había pensado usar el argumento input_parameters de esta manera. Para aquellos cuyas consultas tienen más parámetros que la lista IN, puede usar array_unshift y array_push para agregar los argumentos necesarios al frente y al final de la matriz. Además, prefiero $input_list = substr(str_repeat(',?', count($ids)), 1);
orca
77
También podrías intentarlo str_repeat('?,', count($ids) - 1) . '?'. Una llamada a la función menos.
orca
55
@erfling, esta es una declaración preparada, ¿de dónde vendrá la inyección? Estaré más que feliz de hacer cualquier corrección si puedes respaldar eso con alguna prueba real de eso.
DonVaughn
55
@erfling, sí, eso es correcto, y vincular los parámetros es exactamente lo que estamos haciendo en este ejemplo enviando executeuna serie de identificadores
DonVaughn
55
Oh de hecho. De alguna manera se perdió el hecho de que estaba pasando la matriz. De hecho, esto parece ser seguro y una buena respuesta. Mis disculpas.
erfling
46

¿Es tan importante usar la INdeclaración? Intenta usar FIND_IN_SETop.

Por ejemplo, hay una consulta en PDO como esa

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

Entonces solo necesita vincular una matriz de valores, implosionados con comas, como este

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

Y ya está hecho.

UPD: Como algunas personas señalaron en los comentarios a esta respuesta, hay algunos problemas que deberían mencionarse explícitamente.

  1. FIND_IN_SETno utiliza el índice en una tabla, y todavía no está implementado todavía; consulte este registro en el rastreador de errores MYSQL . Gracias a @BillKarwin por el aviso.
  2. No puede usar una cadena con una coma dentro como valor de la matriz para la búsqueda. Es imposible analizar dicha cadena de la manera correcta después de implodeusar el símbolo de coma como separador. Gracias a @VaL por la nota.

En resumen, si no depende mucho de los índices y no utiliza cadenas con comas para la búsqueda, mi solución será mucho más fácil, más simple y más rápida que las soluciones enumeradas anteriormente.

Tim Tonkonogov
fuente
25
IN () puede usar un índice y cuenta como una exploración de rango. FIND_IN_SET () no puede usar un índice.
Bill Karwin
1
Ese es el punto. No sabia esto. Pero de cualquier manera no hay requisitos para el rendimiento en la pregunta. Para tablas no tan grandes es mucho mejor y más limpio que la clase separada para generar consultas con diferentes números de marcadores de posición.
Tim Tonkonogov
11
Sí, pero ¿quién tiene una mesa no tan grande en estos días? ;-)
Bill Karwin
3
Otro problema con este enfoque es ¿qué pasa si habrá una cadena con una coma adentro? Por ejemplo ... FIND_IN_SET(description,'simple,search')funcionará, pero FIND_IN_SET(description,'first value,text, with coma inside')fallará. Por lo tanto, la función buscará en "first value", "text", "with coma inside" lugar de lo deseado"first value", "text, with coma inside"
VaL
33

Como hago muchas consultas dinámicas, esta es una función auxiliar super simple que hice.

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

Úselo así:

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

Devuelve una cadena :id1,:id2,:id3y también actualiza los $bindArrayenlaces que necesitará cuando llegue el momento de ejecutar su consulta. ¡Fácil!

martillo programador
fuente
66
Esta es una solución mucho mejor, ya que no rompe la regla de enlace de parámetros. Esto es mucho más seguro que tener sql en línea como lo proponen algunos otros aquí.
Dimitar Darazhanski
1
Awesomeness. Elegante. Perfecto.
Ricalsin
17

La solución de EvilRygy no funcionó para mí. En Postgres puedes hacer otra solución:


$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();
Sergey Galkin
fuente
Esto no funciona: ERROR: operator does not exist: integer = text. Al menos necesitas agregar un casting explícito.
collimarco
17

Una manera muy limpia para postgres es usar la matriz postgres ("{}")

$ids = array(1,4,7,9,45);
$param = "{".implode(', ',$ids)."}";
$cmd = $db->prepare("SELECT * FROM table WHERE id = ANY (?)");
$result = $cmd->execute(array($param));
ESCOBAR
fuente
bloquea la inyección sql?
Fábio Zangirolami
1
@ FábioZangirolami es DOP, así que sí.
ESCOBAR
SÍ, PDO! después de 4 años Su respuesta fue buena para mí, muy simple y efectiva. ¡¡¡gracias!!!
Fábio Zangirolami
14

Aquí está mi solución:

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

Tenga en cuenta el uso de array_values. Esto puede solucionar problemas de pedidos clave.

Estaba fusionando matrices de identificadores y luego eliminando elementos duplicados. Tenía algo como:

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

Y eso estaba fallando.

Progrock
fuente
Esta fue una excelente solución. A diferencia de la construcción de la cadena de consulta, esto utiliza el enlace correctamente. Recomiendo este.
Lindylead
Nota: Con su solución, puede agregar elementos al frente o al reverso de la matriz, para que pueda incluir otros enlaces. $original_array Agregar elementos antes de la matriz original: array_unshift($original_array, new_unrelated_item); Agregue elementos después de la matriz original: array_push($original_array, new_unrelated_item); cuando los valores están vinculados, los elementos new_unrelacionados se colocarán en las ubicaciones correctas. Esto le permite mezclar elementos de matriz y no matriz. `
Lindylead
12

Extendí PDO para hacer algo similar a lo que sugiere stefs, y a la larga fue más fácil para mí:

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;
    }
}

Puedes usarlo así:

$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();
Chris
fuente
1
He abordado la primera parte del comentario de Mark, pero como señaló, todavía no es seguro si un token como :arrayestá en una cadena en la consulta.
Chris
44
Una nota para todos los futuros lectores: esta solución nunca debe usarse. Las afirmaciones no están destinadas al código de producción
su sentido común
1
YCS: gracias por los comentarios, interesado en su opinión sobre el enfoque fuera de la idoneidad de las afirmaciones.
Chris
1
La idea es bastante la misma, pero sin afirmaciones y de manera más directa y explícita, no como una excepción para un solo caso, sino como una forma general de construir cada consulta. Cada marcador de posición está marcado con su tipo. Hace if (is_array($data))innecesarias las conjeturas (como una), pero hace que el procesamiento de datos sea mucho más preciso.
Su sentido común
1
Para cualquier persona que lea los comentarios: el problema mencionado por @Your Common Sense se ha solucionado en la revisión 4 .
user2428118
11

En cuanto a PDO: Constantes predefinidas, no hay PDO :: PARAM_ARRAY que necesitaría tal como aparece en PDOStatement-> bindParam

bool PDOStatement :: bindParam (mezclado $ parámetro, mezclado y $ variable [, int $ data_type [, int $ length [, mixed $ driver_options]]])

Así que no creo que sea posible.


fuente
1
No sé si eso funciona. Supongo que la cadena implosionada se cita.
soulmerge
2
Tienes razón, las citas se escapan para que no funcione. He eliminado ese código.
11

Cuando tiene otro parámetro, puede hacer lo siguiente:

$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();
Daniel Miloca - Brasil
fuente
Gracias por la gran respuesta. Este fue el único de todos los que realmente funcionó para mí. Sin embargo, vi 1 error. La variable $ rs debería ser $ stmt
Piet
11

Para mí, la solución más sexy es construir una matriz asociativa dinámica y usarla

// 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);
RousseauAlexandre
fuente
Es difícil estar seguro sin intentarlo en un escenario real, pero parece estar bien. + 1
Anant Singh --- Alive to Die
9

También me doy cuenta de que este hilo es antiguo, pero tuve un problema único en el que, al convertir el controlador mysql que pronto será obsoleto en el controlador PDO, tuve que hacer una función que pudiera construir, dinámicamente, tanto los parámetros normales como los IN desde el mismo param array. Así que rápidamente construí esto:

/**
 * 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;
}

Todavía no se ha probado, sin embargo, la lógica parece estar allí.

Espero que ayude a alguien en la misma posición,

Editar: Después de algunas pruebas descubrí:

  • PDO no le gusta '.' en sus nombres (lo cual es un poco estúpido si me preguntas)
  • bindParam es la función incorrecta, bindValue es la función correcta.

Código editado a la versión de trabajo.

Sammaye
fuente
8

Una pequeña edición sobre el código 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
Aaron Angelo Vicuña
fuente
1
Tuve que eliminar -1 después del conteo ($ ids) para que funcione para mí o siempre faltaría un marcador de posición.
Marcel Burkhard
7

¿Qué base de datos está utilizando? En PostgreSQL me gusta usar CUALQUIERA (matriz). Entonces, para reutilizar su ejemplo:

<?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();
?>

Desafortunadamente, esto es bastante no portátil.

En otras bases de datos necesitarás inventar tu propia magia como otros han mencionado. Querrás poner esa lógica en una clase / función para que sea reutilizable en todo tu programa, por supuesto. Eche un vistazo a los comentarios en la mysql_querypágina de PHP.NET para obtener más ideas sobre el tema y ejemplos de este escenario.

Ryan Bair
fuente
4

Después de pasar por el mismo problema, fui a una solución más simple (aunque todavía no es tan elegante como un PDO::PARAM_ARRAY lo sería):

dada la matriz $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();

... y así

Entonces, si está utilizando una matriz de valores mixtos, necesitará más código para probar sus valores antes de asignar el tipo 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 ));

Pero no he probado este.

alan_mm
fuente
4

Como sé, no hay ninguna posibilidad de vincular una matriz en una declaración PDO.

Pero existen 2 soluciones comunes:

  1. Utilice marcadores de posición posicionales (?,?,?,?) O marcadores de posición con nombre (: id1,: id2,: id3)

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

  2. Matriz de cotizaciones anterior

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

Ambas opciones son buenas y seguras. Prefiero el segundo porque es más corto y puedo var_dump parámetros si lo necesito. Usando marcadores de posición, debe vincular valores y al final su código SQL será el mismo.

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

Y lo último e importante para mí es evitar el error "el número de variables enlazadas no coincide con el número de tokens"

Doctrine es un gran ejemplo del uso de marcadores de posición posicionales, solo porque tiene control interno sobre los parámetros entrantes.

Oleg Matei
fuente
4

Si la columna solo puede contener enteros, probablemente podría hacer esto sin marcadores de posición y simplemente poner los identificadores en la consulta directamente. Solo tiene que convertir todos los valores de la matriz en enteros. Me gusta esto:

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

Esto no debería ser vulnerable a ninguna inyección de SQL.

Kodos Johnson
fuente
3

Aquí está mi solución. También he extendido la clase 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);
        }
    }
}

Aquí hay un ejemplo de uso para el código anterior:

$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'];
}

Déjame saber lo que piensas

Lippai Zoltan
fuente
Olvidé mencionar que esto se basa en la respuesta de user2188977, a continuación.
Lippai Zoltan
No estoy seguro de qué es getOne (), no parece ser parte de PDO. Lo he visto solo en PEAR. ¿Qué hace exactamente?
Lippai Zoltan
@YourCommonSense, ¿puede publicar su función definida por el usuario como respuesta?
nulabilidad
Sugeriría pasar el tipo de datos a BindArrayParamla matriz asociativa, ya que parece limitar esto a los enteros.
Ian Brindley
2

No es posible usar una matriz como esa en PDO.

Necesita construir una cadena con un parámetro (¿o usar?) Para cada valor, por ejemplo:

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

Aquí hay un ejemplo:

<?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 desea seguir usando bindParam, puede hacer esto en su lugar:

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

Si desea utilizar ?marcadores de posición, puede hacerlo así:

<?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 no sabe si $idsestá vacío, debe probarlo y manejar ese caso en consecuencia (devolver una matriz vacía, o devolver un objeto nulo, o lanzar una excepción, ...).

Pedro Amaral Couto
fuente
0

Llevé un poco más lejos para obtener la respuesta más cercana a la pregunta original de usar marcadores de posición para vincular los parámetros.

Esta respuesta tendrá que hacer dos bucles a través de la matriz que se utilizará en la consulta. Pero resuelve el problema de tener otros marcadores de posición de columna para consultas más selectivas.

//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();
Joseph_J
fuente
0

primero establece el número de "?" en consulta y luego por un "para" enviar parámetros como este:

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);
Ali Chegini
fuente