2010-09-13 18 views
13

Imaginez que nous avons une requête:Une instruction préparée, `WHERE .. IN (..)` requête et tri - avec MySQL

SELECT * FROM somewhere WHERE `id` IN(1,5,18,25) ORDER BY `name`; 

et un tableau d'ID chercher: $ids = array(1,5,18,25)

Avec déclarations préparées il est adviced de préparer une déclaration et l'appeler plusieurs fois:

$stmt = $mysqli->prepare('SELECT * FROM somewhere WHERE `id`=?;'); 
foreach ($ids as $id){ 
    $stmt->bind_params('i', $id); 
    $stmt->exec(); 
    } 

Mais maintenant, je vais devoir trier les résultats manuellement. Est-ce que j'ai d'autres bonnes alternatives?

+0

Selon le lien fourni I pense que le raisonnement pour la préparation des déclarations était le besoin de UPDATE qui ne supporte pas plusieurs mises à jour dans une requête. Alors que vous êtes en train de sélectionner et que votre première requête est suffisante. –

+0

J'étais convaincu que les déclarations préparées ne sont pas bonnes dans de tels cas. La seule solution sympa consiste à «faire une requête» et à trier ce tableau d'ID, pas ici. – kolypto

Répondre

19

vous pourriez le faire de cette façon:

$ids = array(1,5,18,25); 

// creates a string containing ?,?,? 
$clause = implode(',', array_fill(0, count($ids), '?')); 


$stmt = $mysqli->prepare('SELECT * FROM somewhere WHERE `id` IN (' . $clause . ') ORDER BY `name`;'); 

call_user_func_array(array($stmt, 'bind_param'), $ids); 
$stmt->execute(); 

// loop through results 

L'utilisation de ce que vous appelez bind_param pour chaque id et vous avez le tri effectué par MySQL.

+1

Oui, mais la mauvaise chose est que la requête ne peut pas être réutilisée donc il n'y a pas besoin de la préparer du tout :) – kolypto

+1

@o_O Tync: Moins les déplacements de base de données, mieux c'est. Vous pouvez vous en tenir aux contraintes PreparedStatement, mais cela ne sera pas échelonné si vous devez exécuter des requêtes 10/20/50/100/1000 + dans la même table. –

+1

Ne vaudrait-il pas mieux créer une vue pour cette instruction puisqu'elle ne peut pas être réutilisée de toute façon? –

0

Non, ce n'est pas recommandé si vous devez extraire certains enregistrements de la base de données en utilisant la clause ORDER BY.

1

Une alternative serait d'utiliser la fonction PHP usort sur l'objet résultat, mais ceci est "manuel".

Voir ceci: Sort Object in PHP

3

Je vais ajouter une solution laide & finalement lente qui utilise néanmoins des déclarations préparées pour un certain nombre d'éléments du tableau :) 3 déclarations sont universels pour tous les cas et peuvent être réutilisés partout.

  1. CREATE TEMPORARY TABLE ids ( id INT);
  2. INSERT INTO ids VALUES(?); ce insérera vos ID
  3. SELECT id FROM ids LEFT JOIN .... ; utiliser des données provenant d'autres tables pour trier la ids liste
  4. SELECT id FROM ids ; sélectionner tout retour

Sinon, vous devrez utiliser IN (?,?,?,.... ou trier les lignes manuellement. La meilleure idée est d'utiliser des requêtes MySQL simples, ou, essayez d'obtenir la liste des ID déjà triés comme vous le souhaitez.

0

Avez-vous envisagé de réécrire votre requête d'origine à l'aide d'une clause JOIN et WHERE pour obtenir l'IDS dont vous avez besoin pour éviter la nécessité d'une clause WHERE IN? Je suis venu ici avec la même question et après avoir examiné les solutions possibles, j'ai réalisé qu'une INNER JOIN était ma solution.

+0

C'était la logique interne: l'application avait besoin de récupérer N utilisateurs par ID, fourni en externe. Heureux que votre cas ne soit pas si spécifique :) – kolypto

5

Je crois que c'est la réponse la plus simple possible:

$ids = [1,2,3,4,5]; 
$pdos = $pdo->prepare("SELECT * FROM somwhere WHERE id IN (:" 
     . implode(',:', array_keys($ids)) . ") ORDER BY id"); 

foreach ($ids as $k => $id) { 
    $pdos->bindValue(":". $k, $id); 
} 

$pdos->execute(); 
$results = $pdos->fetchAll(); 

Tant votre tableau de Ids ne contient pas les clés ou les touches avec des caractères illégaux, il wil travail.

1

eu le même problème et en plus de la réponse @sled il y a 7 ans, voici une possibilité sans faire l'étape call_user_func_array(array($stmt, 'bind_param'), $ids);, mais seulement appeler bind_params fois:

$ids = array(1,5,18,25); 

// creates a string containing ?,?,? 
$bindClause = implode(',', array_fill(0, count($ids), '?')); 
//create a string for the bind param just containing the right amount of iii 
$bindString = str_repeat('i', count($ids)); 

$stmt = $mysqli->prepare('SELECT * FROM somewhere WHERE `id` IN (' . $bindClause . ') ORDER BY `name`;'); 

$stmt->bind_params($bindString, ...$ids); 
$stmt->execute();