71 votes

Valeurs de liaison PDO pour l'instruction MySQL IN

J'ai un problème avec PDO que je voudrais vraiment obtenir une réponse après avoir été tourmenté par elle pendant un certain temps.

Prenez cet exemple:

Je suis de la liaison d'un tableau d'ID pour un AOP déclaration pour une utilisation dans une base de données MySQL DANS l'énoncé.

La matrice serait de dire: $valeurs = array(1,2,3,4,5,6,7,8);

La base de données-fort variable serait $produits = implode(',' $valeurs);

Donc, $produits serait alors une CHAÎNE d'une valeur de: '1,2,3,4,5,6,7,8'

La déclaration ressemblerait à:

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (:products)

Bien sûr, $produits serait lié à la déclaration :produits.

Toutefois, lorsque l'instruction est compilée et de valeurs liées à l', il serait effectivement ressembler à ceci:

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN ('1,2,3,4,5,6,7,8')

Le problème est qu'il est en cours d'exécution tout à l'intérieur de l'énoncé comme une seule chaîne, étant donné que je l'ai préparée comme valeurs séparées par des virgules à se lier à la déclaration.

Ce que j'ai réellement besoin est:

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 seule façon que je peux réellement faire est de placer les valeurs dans la chaîne elle-même, sans liaison entre eux, cependant je sais que pour certains, il y a un moyen plus facile de le faire.

Je dois être complètement à côté de la marque ici et je sais que je vais me lancer quand je vois ce qu'est la réponse!

Merci à l'avance.

38voto

James McNellis Points 193607

C'est la même chose, comme l'a demandé dans cette question: http://stackoverflow.com/questions/920353/php-pdo-can-i-bind-an-array-to-an-in-condition

La réponse a été que, pour une variable de la taille de la liste dans l' in clause, vous aurez besoin de construire la requête de vous-même.

Toutefois, vous pouvez utiliser de la cité, une liste séparée par des virgules à l'aide de find_in_set, bien que pour de grands ensembles de données, cela permettrait d'avoir beaucoup d'impact sur les performances, puisque chaque valeur dans le tableau doit être casté pour un type char.

Par exemple:

select users.id
from users
join products
on products.user_id = users.id
where find_in_set(cast(products.id as char), :products)

Ou, comme une troisième option, vous pouvez créer une fonction définie par l'utilisateur qui divise la liste séparée par des virgules pour vous (cf. http://www.slickdev.com/2008/09/15/mysql-query-real-values-from-delimiter-separated-string-ids/). C'est probablement la meilleure option pour les trois, surtout si vous avez beaucoup de requêtes qui s'appuient sur in(...) clauses.

27voto

ghbarratt Points 4468

Une bonne façon de gérer cette situation est d'utiliser str_pad à la place d'un ? pour chaque valeur dans la requête SQL. Ensuite, vous pouvez passer le tableau de valeurs (dans votre cas $values) comme argument à exécuter:

$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 cette façon, vous obtenez plus de bénéficier de l'aide de déclarations préparées à l'avance plutôt que d'insérer les valeurs directement dans la base SQL.

PS - Les résultats seront de retour en double id utilisateur si les produits avec le id de partager des id utilisateur. Si vous ne voulez unique id d'utilisateur-je suggérer de changer la première ligne de la requête en SELECT DISTINCT users.id

8voto

Asaph Points 56989

La déclaration la mieux préparée que vous pourriez probablement trouver dans une situation comme celle-ci ressemble à ceci:

 SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (?,?,?,?,?,?,?,?) 

Vous pouvez ensuite parcourir vos valeurs et les lier à l'instruction préparée en vous assurant qu'il existe le même nombre de points d'interrogation que les valeurs que vous liez.

1voto

Vunse Points 11

Si l'expression est basée sur une entrée utilisateur sans lier les valeurs avec bindValue (), le SQL expérimental n'est peut-être pas un très bon choix. Mais vous pouvez le rendre sûr en vérifiant la syntaxe de l'entrée avec REGEXP de MySQL.

Par exemple:

 SELECT *
FROM table
WHERE id IN (3,156)
AND '3,156' REGEXP '^([[:digit:]]+,?)+$'
 

1voto

fyrye Points 119

Vous pouvez le faire très facilement. Si vous avez un tableau de valeurs pour votre EG d'instruction IN ():

 $test = array(1,2,3);
 

Vous pouvez simplement faire

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X