Il m’arrive fréquemment d’avoir à sélectionner des lignes de données à partir d’un ou plusieurs critères. C’est le cas dès que l’on souhaite récupérer une liste. C’est également très pratique pour mettre à jour ces dites-lignes. Seulement voilà, des fois on pourrait éviter quelques boucles et quelques complications avec l’utilisation de deux fonctions magiques de MySQL : IN() et GROUP_CONCAT().
Sélection optimisée avec IN()
IN() devrait être utilisé dès lors que l’on a plusieurs critères sur un même colonne. L’écriture suivante ne devrait pas apparaitre dans votre code :
SELECT * FROM `ma_table` WHERE id = 3 OR id = 5;
A la place, il devrait y avoir ceci :
SELECT * FROM `ma_table` WHERE id IN (3, 5);
C’est une habitude à prendre car elle permettra d’automatiser bien des choses. Imaginez qu’on ne fasse plus cette sélection « en dur » mais à partir d’un tableau PHP. Trois façons d’écrire la requête, vous verrez vite laquelle sera la plus pratique à réutiliser :
$tableau = array(3, 5);
/* Méthode 1 : vite-fait mal fait */
$separateur = '';
$sql = "SELECT * FROM `ma_table` WHERE ";
foreach( $tableau as $id )
{
$sql .= $separateur." id = ".$id;
$separateur = ' OR ';
}
/* Méthode 2 : utilisation "optimisée" avec des OR dans la requête */
$sql = "SELECT * FROM `ma_table` WHERE id = ".implode(' OR id = ', $tableau);
/* Méthode 3 : passe-partout */
$sql = "SELECT * FROM `ma_table` WHERE id IN (".implode(',', $tableau).")";
Le gros avantage de la dernière méthode c’est le retraitement des données. Il est facile et plus logique d’imploser et d’exploser une chaine composée de séparateurs virgule (ou autre caractère employé aux mêmes fins).
$tableau = array(3, 5);
$tableau = implode(',', $tableau);
/* du code nécessitant $tableau en tant que chaîne */
$tableau = explode(',', $tableau);
/* du code nécessitant $tableau en tant que tableau */
Aggrégats avec GROUP_CONCAT()
Dans les exemples précédents, $tableau était rempli « en dur ». Dans la vraie vie, ça ne se passe pas comme ça : on récupère des identifiants (clés et/ou index) pour valider les sélections. L’exemple basique : on veut mettre à jour une table de configuration avec les ID d’articles présents dans une ou plusieurs catégories.
$tableau = array();
/* Sélection des ID */
$query = mysql_query("SELECT id FROM `ma_table` WHERE cat_id IN ('fromage', 'tortues');");
while( $ligne = mysql_fetch_assoc($query) )
{
$tableau[] = $ligne['id'];
}
/* On met à jour notre table de configuration */
$tableau = implode(',', $tableau);
mysql_query("REPLACE INTO `table_config` (config_id, config_valeur) ".
"('cache_articles', '{$tableau}')");
C’est simple, propre et on se dit qu’on a bien bossé. Et pourtant, l’utilisation de la fonction d’agrégation GROUP_CONCAT() de MySQL nous épargnera quelques lignes. On appréciera :
$tableau = array();
/* Sélection des ID */
$query = mysql_query("SELECT GROUP_CONCAT(id) AS cache_articles FROM `ma_table` WHERE cat_id IN ('fromage', 'tortues');");
$tableau = mysql_fetch_assoc($query);
/* On met à jour notre table de configuration */
mysql_query("REPLACE INTO `table_config` (config_id, config_valeur) ".
"('cache_articles', '{$tableau['cache_articles']}'");
Bref, on a gagné une boucle (le while), des lignes de résultats MySQL (autant de ressources d’économisées) et un traitement PHP en moins (implode). Et devinez quoi ? Le résultat retourné par le GROUP_CONCAT s’intègre très bien dans le … IN().
Commentaires
Je ne connaissais pas la fonction IN(), et je sais déjà quels fichiers je vais ouvrir pour l’utiliser à la place des multiples ‘OR’
Bonne idée que d’expliquer de façon simple et concrète des fonctions (de mysql ou autre) moins connues. Beaucoup de développeurs, pressés par les délais à tenir, ne font pas de veille et bossent longtemps avec les mêmes méthodes (j’en fais partie), ce qui à long terme devient fort inefficace…
J’attends déjà le prochain billet de ce genre !
C’est difficile de tout apprendre tout seul. Internet est un bon complément à son expérience personnelle et aux ressources papier. Lire et comprendre du bon code permet aussi d’apprendre : la lecture du code de DotClear, WordPress et Invision Power Board ont quelque peu dirigé mes idées.
J’ai la chance de pouvoir gérer mon temps comme bon me semble alors j’en profite pour faire de la veille. L’utilisation de Google Reader a énormément accru mes sources de lecture puisque désormais je ne perds plus aucune information. C’est indispensable lorsqu’on n’est pas tout le temps devant son propre PC.
J’ai prévu à l’avenir de rédiger d’autres billets pratiques pour partager mon expérience. J’ai l’habitude de travailler sur de gros volumes de données (plusieurs centaines de milliers de lignes à croiser dans plusieurs tables) donc du coup je n’ai pas eu d’autre choix que de faire du code et des schémas optimisés
Prochain coup, je pense parler de la redondance de données mais si toi (ou d’autres lecteurs) ont des sujets précis c’est avec plaisir du moment que c’est dans mes cordes.
Justement, à quel(s) flux rss es-tu abonné pour tout ce qui est code/développement ?
Je suis des blogs / sites plus ou moins spécialisés ainsi que des sites d’informatique générale pour me faire une idée des tendances et me faire mes prédictions. En complément je parcours les docs souvent bien garnies ; les commentaires des utilisateurs étant précieux : une page au hasard et ensuite je suis de fil en aiguille (ou en fonction de ce qui peut m’intéresser).
Sinon pour les blogs :
Je ne connaissaispas non plus IN(), voilà qui va m’être très utile… merci j’adore ce genre d’info
On a souvent tendance à n’utiliser que la base de MySQL, alors qu’il y a plein de trucs pour trouver des solutions optimisées.