Declaraciones preparadas de MySQL con una list de variables de tamaño variable

¿Cómo escribirías una statement de MySQL preparada en PHP que toma una cantidad diferente de arguments cada vez? Un ejemplo de tal consulta es:

SELECT `age`, `name` FROM `people` WHERE id IN (12, 45, 65, 33) 

La cláusula IN tendrá una cantidad diferente de id cada vez que se ejecute.

Tengo dos soluciones posibles en mi mente pero quiero ver si hay una mejor manera.

Solución posible 1 Haga que la instrucción acepte 100 variables y complete el rest con valores ficticios que garanticen que no están en la tabla; hacer múltiples llamadas para más de 100 valores.

Posible solución 2 No use una statement preparada; construya y ejecute la consulta comprobando rigurosamente posibles ataques de inyección.

Puedo pensar en un par de soluciones.

Una solución podría ser crear una tabla temporal. Haga una inserción en la tabla para cada parámetro que tendría en la cláusula in. Luego haz una simple combinación contra tu tabla temporal.

Otro método podría ser hacer algo como esto.

 $dbh=new PDO($dbConnect, $dbUser, $dbPass); $parms=array(12, 45, 65, 33); $parmcount=count($parms); // = 4 $inclause=implode(',',array_fill(0,$parmcount,'?')); // = ?,?,?,? $sql='SELECT age, name FROM people WHERE id IN (%s)'; $preparesql=sprintf($sql,$inclause); // = example statement used in the question $st=$dbh->prepare($preparesql); $st->execute($parms); 

Sospecho, pero no tengo testings, que la primera solución podría ser mejor para lists más grandes, y la última funcionaría para lists más pequeñas.


Hacer feliz a @orrd aquí es una versión concisa.

 $dbh=new PDO($dbConnect, $dbUser, $dbPass); $parms=array(12, 45, 65, 33); $st=$dbh->prepare(sprintf('SELECT age, name FROM people WHERE id IN (%s)', implode(',',array_fill(0,count($parms),'?')))); $st->execute($parms); 

También está la function FIND_IN_SET cuyo segundo parámetro es una cadena de valores separados por comas:

 SELECT age, name FROM people WHERE FIND_IN_SET(id, '12,45,65,33') 

envoltorios decentes de sql admiten enlaces a valores de matriz. es decir

 $sql = "... WHERE id IN (?)"; $values = array(1, 2, 3, 4); $result = $dbw -> prepare ($sql, $values) -> execute (); 

recibí mi respuesta de: http://bugs.php.net/bug.php?id=43568
esta es mi solución de trabajo para mi problema. Ahora puedo usar dinámicamente tantos parameters como quiera. Serán el mismo número que tengo en una matriz o como en este caso estoy pasando los ID de la última consulta (que encontró todos los identificadores donde email = 'johndoe@gmail.com') a la consulta dinámica para get todos la información sobre cada uno de estos id. no importa cuántos necesite.

 <?php $NumofIds = 2; //this is the number of ids i got from the last query $parameters=implode(',',array_fill(0,$NumofIds,'?')); // = ?,? the same number of ?'s as ids we are looking for<br /> $paramtype=implode('',array_fill(0,$NumofIds,'i')); // = ii<br/> //make the array to build the bind_param function<br/> $idAr[] = $paramtype; //'ii' or how ever many ?'s we have<br/> while($statement->fetch()){ //this is my last query i am getting the id out of<br/> $idAr[] = $id; } //now this array looks like this array:<br/> //$idAr = array('ii', 128, 237); $query = "SELECT id,studentid,book_title,date FROM contracts WHERE studentid IN ($parameters)"; $statement = $db->prepare($query); //build the bind_param function call_user_func_array (array($statement, "bind_param"), $idAr); //here is what we used to do before making it dynamic //statement->bind_param($paramtype,$v1,$v2); $statement->execute(); ?> 

Por favor toma el # 2 de la table. Las declaraciones preparadas son la única forma en que debe considerar protegerse contra la inyección de SQL.

Sin embargo, lo que puede hacer es generar un set dynamic de variables bindings. es decir, no hagas 100 si necesitas 7 (o 103).

Si solo está utilizando valores integers en su cláusula IN , no hay nada que impida build su consulta dinámicamente sin el uso de parameters SQL.

 function convertToInt(&$value, $key) { $value = intval($value); } $ids = array('12', '45', '65', '33'); array_walk($ids, 'convertToInt'); $sql = 'SELECT age, name FROM people WHERE id IN (' . implode(', ', $ids) . ')'; // $sql will contain SELECT age, name FROM people WHERE id IN (12, 45, 65, 33) 

Pero sin duda la solución aquí es el enfoque más general de este problema.

Tuve un problema similar hoy y encontré este tema. Al mirar las respuestas y search en google encontré una solución bonita.

Aunque, mi problema es un poco más complicado. Porque también he corregido los valores de enlace y la dinámica .

Esta es la solución.

 $params = array() $all_ids = $this->get_all_ids(); for($i = 0; $i <= sizeof($all_ids) - 1; $i++){ array_push($params, $all_ids[$i]['id']); } $clause = implode(',', array_fill(0, count($params), '?')); // output ?, ?, ? $total_i = implode('', array_fill(0, count($params), 'i')); // output iiii $types = "ss" . $total_i; // will reproduce : ssiiii ..etc // %% it's necessary because of sprintf function $query = $db->prepare(sprintf("SELECT * FROM clients WHERE name LIKE CONCAT('%%', ?, '%%') AND IFNULL(description, '') LIKE CONCAT('%%', ?, '%%') AND id IN (%s)", $clause)); $thearray = array($name, $description); $merge = array_merge($thearray, $params); // output: "John", "Cool guy!", 1, 2, 3, 4 // We need to pass variables instead of values by reference // So we need a function to that call_user_func_array('mysqli_stmt_bind_param', array_merge (array($query, $types), $this->makeValuesReferenced($merge))); 

Y la function makeValuesreferenced :

 public function makeValuesReferenced($arr){ $refs = array(); foreach($arr as $key => $value) $refs[$key] = &$arr[$key]; return $refs; } 

Enlaces para get este "know-how": https://bugs.php.net/bug.php?id=49946 , PHP anexa una matriz a otra (no array_push o +) , [PHP]: Error -> Muy pocos arguments en sprintf (); , http://no2.php.net/manual/en/mysqli-stmt.bind-param.php#89171 , Pasa por problema de reference con PHP 5.3.1