php - preparadas - Valores de vinculación PDO para la instrucción MySQL IN
php pdo requirements (8)
Aquí hay un ejemplo de vincular una cantidad desconocida de columnas de registros a los valores de una inserción.
public function insert($table, array $data)
{
$sql = "INSERT INTO $table (" . join('','', array_keys($data)) . '') VALUES (''
. str_repeat(''?,'', count($data) - 1). ''?)'';
if($sth = $this->db->prepare($sql))
{
$sth->execute(array_values($data));
return $this->db->lastInsertId();
}
}
$id = $db->insert(''user'', array(''name'' => ''Bob'', ''email'' => ''[email protected]''));
Tengo un problema con PDO que realmente me gustaría obtener una respuesta después de estar plagado por esto durante bastante tiempo.
Toma este ejemplo:
Estoy vinculando una matriz de ID a una declaración PDO para usar en una declaración MySQL IN.
La matriz sería decir: $ values = array (1,2,3,4,5,6,7,8);
La variable de base de datos segura sería $ products = implode ('','' $ values);
Entonces, $ productos sería una CADENA con un valor de: ''1,2,3,4,5,6,7,8''
La declaración se vería así:
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (:products)
Por supuesto, $ productos estarían vinculados a la declaración como : productos .
Sin embargo, cuando la declaración se compila y los valores se enlazan, en realidad se vería así:
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (''1,2,3,4,5,6,7,8'')
El problema es que está ejecutando todo dentro de la instrucción IN como una sola cadena, dado que lo he preparado como valores separados por comas para enlazar a la declaración.
Lo que realmente necesito es:
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (1,2,3,4,5,6,7,8)
La única forma en que puedo hacer esto es colocando los valores dentro de la cadena misma sin vincularlos, sin embargo, sé con certeza que tiene que haber una manera más fácil de hacerlo.
Esto es lo mismo que se hizo en esta pregunta: ¿Puedo vincular una matriz a una condición IN ()?
La respuesta fue que, para una lista de tamaño variable en la cláusula in
, deberá construir la consulta usted mismo.
Sin embargo, puede usar la lista citada y separada por comas usando find_in_set
, aunque para conjuntos de datos grandes, esto tendría un impacto considerable en el rendimiento, ya que cada valor de la tabla debe convertirse en un tipo de carácter.
Por ejemplo:
select users.id
from users
join products
on products.user_id = users.id
where find_in_set(cast(products.id as char), :products)
O bien, como tercera opción, puede crear una función definida por el usuario que divida la lista separada por comas (consulte http://www.slickdev.com/2008/09/15/mysql-query-real-values-from-delimiter-separated-string-ids/ ). Esta es probablemente la mejor opción de las tres, especialmente si tiene muchas consultas que se basan in(...)
cláusulas.
La frase mejor preparada que probablemente se te ocurra en una situación como esta es algo parecido a lo siguiente:
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (?,?,?,?,?,?,?,?)
Luego recorrerá sus valores y los vinculará a la declaración preparada, asegurándose de que haya la misma cantidad de signos de interrogación que los valores que está vinculando.
Por favor intente así:
WHERE products IN(REPLACE(:products, ''/''', ''''))
Saludos
Puedes hacerlo muy fácilmente. Si tiene una matriz de valores para su declaración IN () EG:
$test = array(1,2,3);
Puedes simplemente hacer
$test = array(1,2,3);
$values = count($test);
$criteria = sprintf("?%s", str_repeat(",?", ($values ? $values-1 : 0)));
//Returns ?,?,?
$sql = sprintf("DELETE FROM table where column NOT IN(%s)", $criteria);
//Returns DELETE FROM table where column NOT IN(?,?,?)
$pdo->sth = prepare($sql);
$pdo->sth->execute($test);
Si la expresión se basa en la entrada del usuario sin vincular los valores con bindValue (), SQL experimental podría no ser una gran opción. Pero puede hacerlo seguro comprobando la sintaxis de la entrada con REGEXP de MySQL.
Por ejemplo:
SELECT *
FROM table
WHERE id IN (3,156)
AND ''3,156'' REGEXP ''^([[:digit:]]+,?)+$''
Una buena forma de manejar esta situación es usar str_pad
para colocar un ?
para cada valor en la consulta SQL. Luego puede pasar la matriz de valores (en su caso, $values
) como argumento para ejecutar:
$sql = ''
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products.id IN (''.str_pad('''',count($values)*2-1,''?,'').'')
'';
$sth = $dbh->prepare($sql);
$sth->execute($values);
$user_ids = $sth->fetchAll();
De esta forma, obtendrá más beneficio del uso de declaraciones preparadas en lugar de insertar los valores directamente en el SQL.
PD: los resultados devolverán identificadores de usuario duplicados si los productos con los identificadores proporcionados comparten identificadores de usuario. Si solo desea ID de usuario únicos, le sugiero cambiar la primera línea de la consulta a SELECT DISTINCT users.id
necesita proporcionar la misma cantidad de ? s en IN como el número de valores en su matriz $ values
esto se puede hacer fácilmente creando una matriz de? s como
$in = join('','', array_fill(0, count($values), ''?''));
y usa este $ in array en tu cláusula IN
Esto te proporcionará dinámicamente una variedad de s de acuerdo con tu matriz de valores de $ changiing.