Afficher nombres d'enregistrements sans faire X requêtes

Avatar du membre
ViPHP
ViPHP | 3008 Messages

05 févr. 2006, 20:15

Bonsoir !

Je vous expose mon problème. J'ai une table où se trouve 2 champs :
categorie et sous catégorie.

Sur une page, j'affiche les catégories et sous catégories de la sorte :
Catégorie :
- ss cat 1
- ss cat 2
- ..etc

Je désire maintenant afficher en face de chaque sous catégorie le nombre d'enregistrements. Comment faire cela sans pour autant faire X requêtes différentes ?

Code : Tout sélectionner

$rqt=mysql_query("SELECT * FROM xxx WHERE cat=1 AND sscat=1"); $nombreligne=mysql_num_rows($rqt); . . . . $rqtX=mysql_query("SELECT * FROM xxx WHERE cat=y AND sscat=z"); $nombreligne20=mysql_num_rows($rqtX);
Merci !

Modérateur PHPfrance
Modérateur PHPfrance | 7636 Messages

05 févr. 2006, 21:00

en moins de 2 requetes je ne pense pas que ce soit possible.

une requete en regroupant par catégories pour compter le nombre de sous-cat.
$sql1="SELECT COUNT(sscat) as nb FROM xxx  GROUP BY cat ORDER BY cat";

$sql2="SELECT cat, sscat FROM xxx ORDER BY cat";
ensuite initialisé une varaible avec le 1er résulats de $sql1 puis parcourir le résulat de $sql2 en effectuant un test sur la catégorie pour verifier si elle change ou pas et afficher le nombre de sous cat. en fonction.

s'il y a changement de catégorie il faut relire une ligne de résulktat de $sql1

/!\ Avant de poster se documenter et rechercher.
Qui ne sait pas rendre un service n'a pas le droit d'en demander.
MaBrute

Avatar du membre
ViPHP
ViPHP | 3008 Messages

05 févr. 2006, 21:44

Je t'avoue que là je suis...largué :roll:

Et comment procèdes-tu ensuite pour afficher en face de chaque catégorie (sachant que l'affichage est en "dure") ?

Merci :)

Mammouth du PHP | 19672 Messages

05 févr. 2006, 22:23

Suggestion : ça vaut ce que ça vaut, mais je l'ai utilisé pour créer un menu contenant des rubriques/sous/rubriques avec une suele requête :
- 1 : je récupère tout classé par rubrique/sous-rubrique
- 2 : j'enregistre les données dans un tableau à deux dimensions : la première dimension, c'est le niveau "rubrique" et la seconde dimension contient les sous-rubriques
- 3 : un simple count($tableau['rubrique']) va me donner le nombre de sous-rubrique
- 4 : j'utilise les valeurs du tableau pour gérer mon affichage au lieu des fonctions mysql déjà utilisées pour créer mon tableau.
Exemple (à tester):
<?php
/* On crée la requête SQL */
$sql = "SELECT `rub_nom`, `ssrub_nom` ".
       "FROM `rubrique` AS r, `ssrubrique` AS sr ".
       "WHERE r.rub_id = sr.rub_id;";
/* Connexion à MySQL et exécution de la requête */
/*
.........
*/
$exec = mysql_query($sql);

$tableau = array();
/* On crée un pointeur pour la première dimension du tableau */
$rub = "";
while(false($ligne = mysql_fetch_assoc($exec)))
{
    if($rub != $ligne['rub_nom'])
    {
        $rub = $ligne['rub_nom'];
        $tableau['rub_nom'] = array();
    }
    $tableau['rub_nom'][] = $ligne['ssrub_nom'];
}
/* Maintenant, j'ai toutes mes lignes classées, je peux afficher */
?>
<ul>
<?php
foreach($tableau as $rubrique => $val)
{
    /* Affichage de la rubrique */
?>
  <li>
<?php
    echo($rubrique ." (". count($tableau[$rubrique]) ." sous-rubriques)");
?>
    <ul>
<?php
    foreach($tableau[$rubrique] as $ssrubrique)
    {
?>
      <li><?php echo($ssrubrique); ?></li>
<?php
    }
?>
    </ul>
  </li>
<?php
}
?>
</ul>
J'ai fait un test en alimentant un tableau à la main avec un tableau comme ceci:
<?php
$tableau = array("rub1" => array("ssrub_11", "ssrub_12", "ssrub_13", "ssrub_14", "ssrub_15"), "rub2" => array("ssrub_21", "ssrub_22"), "rub3" => array("ssrub_31", "ssrub_32", "ssrub_33"))
?>
L'affichage me donne ceci:
  • rub1 (5 sous-rubriques) :
    • ssrub_11
    • ssrub_12
    • ssrub_13
    • ssrub_14
    • ssrub_15
  • rub2 (2 sous-rubriques) :
    • ssrub_21
    • ssrub_22
  • rub3 (3 sous-rubriques) :
    • ssrub_31
    • ssrub_32
    • ssrub_33
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Modérateur PHPfrance
Modérateur PHPfrance | 7636 Messages

05 févr. 2006, 22:45

eh bien voila en gros je pensais à quelque chose du même style sauf que les 2 boucles foreach seraient des while sur le résultat da la requete.

j'ai pas testé ton code Cyrano, mais il n'y aurai pas confusion pour l'indexation:
$tableau['rub_nom'] = array();
au lieu de
$tableau[$rub] = array();
et du même coup:
$tableau['rub_nom'][] = $ligne['ssrub_nom']; 
au lieu de
$tableau[$rub][] = $ligne['ssrub_nom']; 
faut bien qu'il vari cet indice, ou alors j'ai loupé une ligne 8-[

/!\ Avant de poster se documenter et rechercher.
Qui ne sait pas rendre un service n'a pas le droit d'en demander.
MaBrute

Mammouth du PHP | 19672 Messages

05 févr. 2006, 23:30

Ça revient exactement au même... :-k

Par contre ma seconde boucle foreach n'est pas appropriée, voici une correction:
<?php
/* On crée la requête SQL */
$sql = "SELECT `rub_nom`, `ssrub_nom` ".
       "FROM `rubrique` AS r, `ssrubrique` AS sr ".
       "WHERE r.rub_id = sr.rub_id;";
/* Connexion à MySQL et exécution de la requête */
/*
.........
*/
$exec = mysql_query($sql);

$tableau = array();
/* On crée un pointeur pour la première dimension du tableau */
$rub = "";
while(false !== ($ligne = mysql_fetch_assoc($exec)))
{
    if($rub != $ligne['rub_nom'])
    {
        $rub = $ligne['rub_nom'];
        $tableau['rub_nom'] = array();
    }
    $tableau['rub_nom'][] = $ligne['ssrub_nom'];
}
/* Maintenant, j'ai toutes mes lignes classées, je peux afficher */
?>
<ul>
<?php
foreach($tableau as $rubrique => $val)
{
    /* Affichage de la rubrique */
?>
  <li>
<?php
    $nb = count($tableau[$rubrique]);
    echo($rubrique ." (". $nb ." sous-rubriques) :");
?>
    <ul>
<?php
    for($i = 0; $i < $nb; $i++)
    {
?>
      <li><?php echo($tableau[$rubrique][$i]); ?></li>
<?php
    }
?>
    </ul>
  </li>
<?php
}
?>
</ul>
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Modérateur PHPfrance
Modérateur PHPfrance | 7636 Messages

06 févr. 2006, 00:17

Ça revient exactement au même... :-k
Permet moi d'insister mais l'indice sera toujours le même "rub_nom" qui est en dur, il est égal au nom du champ de la catégorie mais n'est pas vraiable.
ne pas me taper sur la tête, hein [-o< :lol:


mais je suis sur que charabia donnera le fin mot de l'histoire lorsqu'il aura testé :D

/!\ Avant de poster se documenter et rechercher.
Qui ne sait pas rendre un service n'a pas le droit d'en demander.
MaBrute

Mammouth du PHP | 19672 Messages

06 févr. 2006, 02:32

Les paris sont ouverts :langue:
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Administrateur PHPfrance
Administrateur PHPfrance | 3088 Messages

06 févr. 2006, 08:35

Désolé de ne pas rentrer dans les détails (mes corn-flakes sont en train de ramollir) mais voici quelques infos/commentaires qui devraient intéresser charabia. Si tu as besoin de précisions n'hésite pas.
[Comment faire pour] afficher en face de chaque sous catégorie le nombre d'enregistrements [...]
Tu pourrais le faire en une seule requête, mais elle serait assez moche et pas forcément plus rapide (voire carrément plus lente). Le mieux est d'en faire deux: la première récupère les noms et les IDs des catégories à afficher (dans l'ordre) et la seconde les sous-catégories qui leur sont affiliées. J'imagine que tu utilises deux tables étant donné que tu n'as pas posté la structure de tes données:

Code : Tout sélectionner

SELECT c.cat_id, c.cat_name, COUNT(sc.ss_cat_id) AS ss_cat_cnt FROM categories c LEFT JOIN ss_categories sc ON sc.cat_id = c.cat_id ORDER BY cat_order

Code : Tout sélectionner

SELECT ss_cat_id, ss_cat_name FROM ss_categories WHERE cat_id IN (...) ORDER BY ss_cat_order
Pourquoi utiliser deux requêtes au lieu d'une ? Je n'ai pas d'explication unique, je tire cet enseignement d'observations. Une des explications est la réduction de la quantité de données qui est envoyé par le serveur. En effet, dans 99,99% des cas on se retrouve obligé de récupérer le nom de la catégorie en même temps que le nom de la sous-catégorie. Du coup, le nom de la catégorie ainsi que toute autre champs requêté se retrouve dans le résultat => plus de traffic, plus de champs à mettre dans la variable PHP qui sert de container aux données, etc...

Comme autres explications on pourrait aussi imaginer la réduction de données, mais cette fois-ci au niveau du tri des enregistrements => tuple plus gros == tri plus lent, même si ce n'est plus vraiment vrai depuis MySQL 4.1. On pourrait ajouter l'overhead dû à la jointure ou une autre demi-douzaine de facteurs...

Je n'ai pas testé cette requête mais si tu tiens absolument à le faire en une seule requête, elle ressemblera probablement à cela: (ne satisfait pas ONLY_FULL_GROUP_BY)

Code : Tout sélectionner

SELECT c.cat_id, c.cat_name, COUNT(sc2.sub_cat_id) AS sub_cat_cnt, sc.sub_cat_id, sc.sub_cat_name FROM categories c, sub_categories sc, sub_categories sc2 WHERE sc.cat_id = c.cat_id AND sc2.cat_id = c.cat_id GROUP BY c.cat_id, sc.cat_id
ou cela (devrait être ANSI, TRADITIONAL)

Code : Tout sélectionner

SELECT tmp.cat_id, tmp.cat_name, tmp.sub_cat_cnt, sc.sub_cat_id, sc.sub_cat_name FROM ( SELECT c.cat_id, c.cat_name, COUNT(sc2.sub_cat_name) AS sub_cat_cnt FROM categories c, sub_categories sc2 WHERE c2.cat_id = c.cat_id GROUP BY c.cat_id ) AS tmp, sub_categories sc WHERE sc.cat_id = c.cat_id ORDER BY c.cat_order, sc.sub_cat_order

Administrateur PHPfrance
Administrateur PHPfrance | 3088 Messages

06 févr. 2006, 08:44

@Cyrano: tu devrais essayer
while ($ligne = mysql_fetch_assoc($exec))
au lieu de
while(false !== ($ligne = mysql_fetch_assoc($exec)))
Étant donné que mysql_fetch_assoc() ne peut pas renvoyer d'array vide, il n'y a aucun risque pour que $ligne soit équivalent à "false" sans y être identique. Ça n'engage que moi, mais c'est plus clair et plus rapide. Plus rapide parce que les opérateurs de comparaison stricte sont marginalement plus lents, et plus clair car la ligne contient moins de bruit. D'ailleurs, dans le même registre je recommande de séparer les structures de contrôle par un espace de façon à les différencier des fonctions.

Avatar du membre
ViPHP
ViPHP | 3008 Messages

06 févr. 2006, 10:42

Pfiou ça en devient un débat c'est génial :langue:

Merci pour toutes ces infos ;)

Hubert Roksor, pour la table je n'ai hélàs qu'une table. Je n'avais pas fait deux ne voyant pas trop l'utilité au départ. Avec ton système, serait-il possible de faire la même chose avec une seule table ?

Pour ce qui est du nombre de requêtes, en fait j'en voudrais le moins possible. J'avais mis "1" juste comme ça :)

Administrateur PHPfrance
Administrateur PHPfrance | 3088 Messages

06 févr. 2006, 11:23

Mmh, oui, il faut juste changer les noms des champs :lol:
Je te donne un exemple simple, on pourrait optimiser le SQL mais le mieux c'est encore de mettre le résultat final en cache.
<?php

$filename = 'cache/data.categories.php';
if (file_exists($filename))
{
	$cats = include($filename);
}
else
{
	$sql = 'SELECT c.cat_id, c.cat_name, COUNT(c2.cat_id) AS children_cnt
			FROM categories c, categories c2
			WHERE c2.parent_id = c.cat_id
			GROUP BY c.cat_id
			ORDER BY c.cat_order';
	$result = $mysqli->query($sql);

	$cats = array();
	while ($row = $result->fetch_assoc())
	{
		$cats[$row['cat_id']] = $row;
	}
	$result->close();

	$sql = 'SELECT cat_id, parent_id, cat_name
			FROM categories
			WHERE parent_id IN (' . implode(',', array_keys($cats)) . ')
			ORDER BY cat_order';
	$result = $mysqli->query($sql);

	while ($row = $result->fetch_assoc())
	{
		$cats[$row['parent_id']]['subs'][$row['cat_id']] = $row['cat_name'];
	}
	$result->close();

	file_put_contents($filename, '<?php return ' . var_export($cats, TRUE) . '; ?>');
}

?>
Je n'ai absolument pas testé ce code, mais ça devrait marcher quand même :) À chaque mise à jour de la table des catégories (ajout, suppression, changement de nom ou d'ordre) tu n'as qu'à effacer le fichier de cache.

Ah, au fait, je ne t'explique pas le format de $cats, il est assez évident donc tu n'as qu'à faire un print_r().

Avatar du membre
ViPHP
ViPHP | 3008 Messages

06 févr. 2006, 12:06

Oki merci beaucoup !

Je verrais tout ça neurone reposé :p

Modérateur PHPfrance
Modérateur PHPfrance | 7636 Messages

06 févr. 2006, 14:53

Hé mais qui va vérifier mes dires maintenant :cry:

On dit match nul :lol:

/!\ Avant de poster se documenter et rechercher.
Qui ne sait pas rendre un service n'a pas le droit d'en demander.
MaBrute

Administrateur PHPfrance
Administrateur PHPfrance | 3088 Messages

06 févr. 2006, 16:36

Hé mais qui va vérifier mes dires maintenant :cry:
Euh... lesquels ? $tableau[$rub][] = $ligne['ssrub_nom']; ? oui, ça ressemble bel et bien à une typo.