Étude de cas PHP & MySQL : Connaître le nombre d’occurrences dans une table
Par Méthylbro le mercredi 21 octobre 2009, 13:00 - Tutoriels - Lien permanent
Toujours plongé dans de l'analyse de code depuis quelques jours, je vous propose à nouveau de partager et d'analyser quelques perles avec vous.
Dans ce billet, je vais m'entretenir avec vous d'un cas récurrent en développement web : compter un nombre d'occurrences spécifique dans une base de données ou plus simplement, connaître le nombre d'occurrences enregistré dans une table. C'est ce dernier exemple que nous utiliserons pour illustrer mes propos.
Parmi mes pérégrinations dans les méandres de ce script infâme ; je suis tombé sur diverses méthodes pour compter un nombre d'occurrences dans une base de données qui m'ont fait hurler.

Premier exemple, le développeur qui n'a jamais appris autre chose que les requêtes de base et se limite aux outils les plus simples de PHP :
$sql = "SELECT * FROM maTable";
$res = mysql_query($sql);
while($row[] = mysql_fetch_array($res));
$nb = count($row);
Remarquez comme c'est magnifique ; lancer une boucle à l'exécution alors que c'est complètement inutile.
Comme deuxième exemple, je vais vous proposer une variante ; le développeur qui n'y connais toujours rien au SQL de base, mais qui c'est quand même penché un minimum sur la documentation de PHP :
$sql = "SELECT * FROM maTable";
$res = mysql_query($sql);
$nb = mysql_num_rows($res);
C'est presque acceptable. Cependant imaginez le nombre d'informations inutiles que vous allez faire circuler entre le serveur de données (ici MySQL) et votre script ? Si le nombre d'enregistrements dans votre table est conséquent, imaginez le temps que cela va prendre !
Bien évidement, vous avez la réponse en vous. Il faut utiliser la fonction SQL COUNT(). C'est logique. Comme cela on ne récupère que le nombre d'occurrences ; après tout pourquoi pas, c'est la seule chose que l'on veut non ?
En bon développeur consciencieux ; vous écrirez sans doutes quelque chose comme cela :
$sql = "SELECT COUNT(*) FROM maTable";
$res = mysql_query($sql);
$row = mysql_fetch_row($res);
$nb = $row[0];
Cependant même si vous êtes presque dans le vrai, une dernière chose me chipote sur cette solution. Pourquoi faire une affectation par ligne avec un fetch
? Il n'y a qu'une seule information dans le résultat votre requête, vous savez ou elle se trouve alors pourquoi ne pas aller la chercher directement ?
C'est pourquoi, et pour conclure, je proposerais tout simplement cette solution :
$sql = "SELECT COUNT(*) AS `nb` FROM `maTable`;";
$res = mysql_query($sql);
$nb = mysql_result($res, 0, 'nb');
Et vous, vous feriez comment ?
Commentaires
Moi j'aime bien utiliser SQL_CALC_FOUND_ROWS puis FOUND_ROWS() notamment pour les requêtes avec un LIMIT pour avoir le nombre total d'enregistrement sans la limite.
@mogito : Intéressant, un petit exemple d'utilisation serait le bienvenue
En attendant un exemple plus précis (ce soir vacances donc .... ^^), la doc explique très bien le principe
http://dev.mysql.com/doc/refman/5.0...
D'accord pour ne pas parcourir le tableau de résultats histoire de compter le nombre (de résultats).
Par contre, je me pose une question...
Je suis développeur ABAP (langage propriétaire SAP) et pour améliorer les performances, on cherche le plus possible à solliciter la couche base de données. Dans l'exemple de cet article, on évitera donc les COUNT dans les requêtes, pour leur préférer l'instruction retournant le nombre d'enregistrement d'un tableau.
Ma question est la suivante : en PHP/MySQL, est-il préférable de faire faire certains traitements par le SGBD (COUNT, ORDER BY, etc.) ou vaut-il mieux faire faire ces traitements par PHP ?
Si vous avez des infos là-dessus, je suis preneur.
@BW HadèS : Super pertinent comme remarque !
Cela mériterais presque un billet complet pour apporter un début de réponse.
Mais pour faire simple : il n'y a pas de règle. C'est une décision à prendre au cas par cas.
Oui, à bien y réfléchir, je pense que ça dépend aussi beaucoup de l'architecture du serveur. Puissance CPU du serveur s'exécute le PHP et du serveur sur lequel tourne le SGBD (si différents), nombre de requêtes potentielles simultanées, volumétrie, etc.
Mais à mon avis ça ne doit pas être aisé de traiter le sujet au cas par cas, ce serait cool d'avoir des "valeurs de référence".
@BW Hadès : oui l'architecture va fortement influencer ces choix, après il y'a plusieurs petits paramètres qui peuvent rentrer en compte.
Généralement les traitement de calculs sont plus rapides avec le SGBD j'ai remarqué, de même que les requêtes multiples en trigger sont bcp plus performantes que plusieurs requêtes à suivre en PHP.
Mais il faut savoir que pour les calculs on peut avoir des erreur de quelques dixième entre un calcul SGBD et un calcul PHP, c'est le cas avec MySQL, j'ai rencontré le cas plusieurs fois au taf. Les recherches sur des champs textes peuvent etre plus performantes avec un filtre PHP que dans la requetes suivant la taille du champ (varchar, text ...) et surtout s'il possède un index ou pas et de quel longueur est l'index.
Pour moi c'est dur de savoir s'il est préférable de choisir les traitements directement dans le SGBD ou dans le code (PHP)
Bonjour
petite question : y a t-il des différences de performance entre COUNT(*) et COUNT(id) par exemple ?
Hello aeon,
Question très intéressante et là aussi comme le disait méthylbro plus haut, ca pourrait être sujet à un article entier.
Un count(*) avec myisam déjà, sera plus rapide qu'un count(*) avec innodb, myisam ayant un cache du nombre de colonne, et pas innodb
Ensuite un count(val) peut etre aussi rapide comme moins rapide, tout dépend comment est défini ton champ.
Si tu as un champ val1 en int unisgned et un champ val2 en int unsigned not null, le count(val2) sera plus rapide car mysql n'a pas a checké la table pour déterminer les champs null.
De même si ta colonne est un index ca sera plus rapide
Bref c'est un grand sujet ^^
Il y a encore une autre solution que je pense être la meilleure :
SHOW TABLE STATUS FROM ma_base LIKE 'ma_table'
Même pas besoin de compter ce qu'il y a dans la table pour connaitre le nombre d'enregistrement qu'il y a dedans .
@bembu : Ton alternative est intéressante.
Cependant si mes souvenirs son exacts (c'est a vérifier) SHOW STATUS et SHOW TABLE STATUS utilisent en réalités les données contenues dans la base information_schema.
Ce qui suppose donc que ces informations soient correctes mais également que tu ai bien accès à ces informations.
Mais c'est plus intéressant en effet car au final tu ne compte rien, tu ne fait que sélectionner une information déjà existante.