Problème avec SUM

Mammouth du PHP | 545 Messages

07 nov. 2007, 10:37

Bonjour,

Voila je suis coincé dans mon développement, je cherche à addition des valeurs directement dans une requête (sur plusieurs tables) et je ne reçois pas ce que j'attends !

Voici mes tables utilisées

Code : Tout sélectionner

CREATE TABLE IF NOT EXISTS `#__classement` ( `id` INT NOT NULL AUTO_INCREMENT, `course_id` INT(11) NOT NULL, `dossard` SMALLINT(5) NOT NULL, `temps` TIME NOT NULL, `place` INT(11) NOT NULL, `moyenne` time NOT NULL, `vitesse` decimal (4,3) NOT NULL, `point` int (11), PRIMARY KEY(`id`) ) TYPE=MyISAM; CREATE TABLE IF NOT EXISTS `#__classement_course` ( `id` INT NOT NULL AUTO_INCREMENT, `course` VARCHAR(45) NOT NULL, `date` DATE NOT NULL, `pat_chal` ENUM ('C','P','H') NOT NULL, `lieu` VARCHAR(45) NOT NULL, `kilometrage` FLOAT(5,3) NOT NULL, `nbre_part` INT(11) NOT NULL, `epreuve_id` INT(11) NOT NULL default '0', PRIMARY KEY(`id`) ) TYPE=MyISAM;
J'aimerai retirer un nombre de total de points pour une personne dans une année précise ... cette personne est identifié avec son dossard pour l'année !

J'ai d'abord commencé par sélectionner toutes les courses
/////////// Les meilleurs courses challenge de l'année pour le coureur X /////////
    $query = "SELECT clas.id as clas_id, clas.point, clas.course_id, clas.dossard, "
        . "\n course.id, course.date as days, course.pat_chal"    
        . "\n FROM #__classement AS clas"
        . "\n INNER JOIN #__classement_course AS course ON course.id = clas.course_id"
        . "\n WHERE DATE_FORMAT(course.date,'%Y') = '2004' AND clas.dossard = '195' AND course.pat_chal = 'C'"
        ;
    $database->setQuery( $query );
    $challengelist = $database -> loadObjectList();
print_r($challengelist); 
Ce qui me renvoie ce que j'attendais:
Array (
[0] => stdClass Object ( [clas_id] => 253 [point] => 608 [course_id] => 2 [dossard] => 195 [id] => 2 [days] => 2004-02-28 00:00:00 [pat_chal] => C )
[1] => stdClass Object ( [clas_id] => 2394 [point] => 577 [course_id] => 1 [dossard] => 195 [id] => 1 [days] => 2004-03-27 00:00:00 [pat_chal] => C )
[2] => stdClass Object ( [clas_id] => 4118 [point] => 458 [course_id] => 61 [dossard] => 195 [id] => 61 [days] => 2004-06-12 16:00:00 [pat_chal] => C ) )
On voit là qu'il y a 3 courses et que lorsque l'on additionne les points (608, 577, 458) on obtient 1693 points ... pour cela en php, il n'y a pas de problème mais je dois impérativement faire cela en sql pour stocker dans un tableau array() !

Après recherche, il faut employer la fonction SUM(). Je travaille donc ma requête:
$query = "SELECT clas.id as clas_id, SUM(clas.point), clas.dossard, clas.course_id, "
        . "\n course.id, course.date as days, course.pat_chal"    
        . "\n FROM #__classement AS clas"
        . "\n INNER JOIN #__classement_course AS course ON course.id = clas.course_id"
        . "\n WHERE DATE_FORMAT(course.date,'%Y') = '2004' AND clas.dossard = '195' AND course.pat_chal = 'C'"
        . "\n GROUP BY clas.point"
        ;
    $database->setQuery( $query );
    $pointchal = $database -> loadResult();
print_r($challengelist);  
Et là rien ne va plus ... j'ai un retour de
4118
soit le clas_id du dernier enregistrement ... quelqu'un peut-il m'aider parce que là cela fait assez longtemps que je navigue en eaux profondes !

Merci
Sebe

Pour moi, le PHP est une nouvelle aventure qui a commencée fin octobre 2005 ... c'est plus exitant que le HTML!

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 13231 Messages

07 nov. 2007, 10:47

Quelle est, selon toi, l'utilité de ton GROUP BY ?
Est-ce que tu veux obtenir les résultats groupés par le nombre de point ? ;)
Connaître son ignorance est la meilleure part de la connaissance
Pour un code lisible : n'hésitez pas à sauter des lignes et indenter

twitter - site perso - Github - Zend Certified Engineer

Mammouth du PHP | 545 Messages

07 nov. 2007, 12:08

Salut,
Quelle est, selon toi, l'utilité de ton GROUP BY ?
Est-ce que tu veux obtenir les résultats groupés par le nombre de point ? ;)
Si le but est de faire la somme de 'clas.point', il ne faut pas mettre cette zone dans le group by car il y aura une ligne par 'point', et non la somme ... ok mais alors qu'est-ce que je dois mettre parce que si je mets tous les autres champs, le résultat n'est toujours pas celui attendu !

Je galère sur ce truc :evil:


Merci
Sebe

Pour moi, le PHP est une nouvelle aventure qui a commencée fin octobre 2005 ... c'est plus exitant que le HTML!

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 13231 Messages

07 nov. 2007, 12:19

Si, dans une SELECT, tu as champ1, champ2, champ3 et que tu veux la somme du champ3 pour chaque couple champ1, champ2, il faut faire un GROUP BY sur ces 2 champs.

J'ai du mal à l'expliquer, mais c'est logique, pour avoir la somme pour chaque tuple (champ1, champ2), il faut grouper les enregistrements de la table pour obtenir un groupe pour chaque tupe (champ1, champ2)
Connaître son ignorance est la meilleure part de la connaissance
Pour un code lisible : n'hésitez pas à sauter des lignes et indenter

twitter - site perso - Github - Zend Certified Engineer

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

07 nov. 2007, 12:29

Peut être quelques explciations différentes quant à l'usage du group by pour te permettre de mieux comprendre comment il fonctionne :

La clause GROUP BY permet de regrouper par champs les données retournés par une requête. Elle ne s'utilise que lorsque vous faites appel à des fonctions de regroupements (telles que AVG(), COUNT(), SUM() ...). Elle doit porter sur l'ensemble des champs présents dans le select qui n'ont pas été groupés par une fonction.

Code : Tout sélectionner

SELECT idForum, nomForum, COUNT(idSujet) AS nbSujet, MAX(idMessage) AS lastPostId FROM ... WHERE ... GROUP BY idForum, nomForum
On obtient ici pour chaque couple (idForum, nomForum) unique, le nombre de "idSujet" ainsi que le plus grand idMessage associés à notre groupe.

HTH :)
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Mammouth du PHP | 545 Messages

07 nov. 2007, 13:09

Merci pour vos explications mais comme cela à chaud, je ne comprend pas ... c'est peut-être un coté blond enfouit dont je n'avais pas la connaissance 8-)

Je vais essayé de digérer vos explications ... je vous tiens au courant !

A+ et merci
Sebe

Pour moi, le PHP est une nouvelle aventure qui a commencée fin octobre 2005 ... c'est plus exitant que le HTML!

Mammouth du PHP | 545 Messages

07 nov. 2007, 13:44

Pendant un moment j'avais cru comprendre qu'il fallait mettre dans le GROUP BY tous les autres champs que l'on avait pas besoin :
. "\n GROUP BY clas.id, clas.dossard, clas.course_id, course.id, course.date as days, course.pat_chal"
mais plus rien n'est renvoyé !
Sebe

Pour moi, le PHP est une nouvelle aventure qui a commencée fin octobre 2005 ... c'est plus exitant que le HTML!

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 13231 Messages

07 nov. 2007, 13:45

Affiche tes messages d'erreur ;)

il ne faut pas de AS dans un GROUP BY
Connaître son ignorance est la meilleure part de la connaissance
Pour un code lisible : n'hésitez pas à sauter des lignes et indenter

twitter - site perso - Github - Zend Certified Engineer

Mammouth du PHP | 545 Messages

07 nov. 2007, 15:08

Affiche tes messages d'erreur ;)
Je n'en ai pas !
il ne faut pas de AS dans un GROUP BY
Ok, enlevé !
Sebe

Pour moi, le PHP est une nouvelle aventure qui a commencée fin octobre 2005 ... c'est plus exitant que le HTML!

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

07 nov. 2007, 16:52

En fait, si tu l'applique bêtement à ta requête, le group by doit ressembler à ça :

Code : Tout sélectionner

SELECT clas.id as clas_id, SUM(clas.point), clas.dossard, clas.course_id, course.id, course.date as days, course.pat_chal ... GROUP BY clas_id, clas.dossard, clas.course_id, course.id, days, course.pat_chal
On retrouve tous les champs non groupés présents dans le select.

Le résultat obtenu sera une liste d'enregistrements distinct contenant toutes les combinaisons de valeurs possible du group by, et la somme des points associée à chacune :)

Maintenant par rapport au résultat que tu attends, il être interessant de retirer l'id du select et du group by (en effet si celui est unique pour chaque enregistrement, il ferait office de niveau le plus fin pour le groupe). En gros, plus tu vas mettre d'éléments dans le SELECT et le GROUP BY, plus le résultat retourné par la SUM sera au plus fin... ex :

Code : Tout sélectionner

SELECT annee, SUM(points) ... GROUP BY annee -- te donne le nombre de points par an SELECT mois, SUM(points) ... GROUP BY mois -- te donnera le nombre de points par mois, quel que soit l'année SELECT annee, mois, SUM(points) ... GROUP BY annee, mois -- te donnera le nombre de points par mois et par année SELECT id, annee, mois, SUM(points) ... GROUP BY id, annee, mois -- te donnera le nombre de points par id, par mois et par année
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Mammouth du PHP | 545 Messages

08 nov. 2007, 00:07

Ryle,

Comme j'ai besoin du nombre de point sur une année pour un dossard, je dois sélectionner ceci:
$query = "SELECT SUM(clas.point) as somme, clas.dossard, "
  . "\n course.date"    
  . "\n FROM #__classement AS clas"
  . "\n INNER JOIN #__classement_course AS course ON course.id = clas.course_id"
  . "\n WHERE DATE_FORMAT(course.date,'%Y') = '2004' AND clas.dossard = '195' AND course.pat_chal = 'C'"
  . "\n GROUP BY clas.dossard, course.date"
  ;
$database->setQuery( $query );
$pointchal = $database -> loadResultArray();
Array ( [0] => 608 [1] => 577 [2] => 458 )
C'est pas encore cela que j'attendais mais c'est déjà mieux ! ? !

Juste une petite question, la façon dont je saisies l'année et le champ dans le group by ne posent pas de problème ?

Merci
Sebe

Pour moi, le PHP est une nouvelle aventure qui a commencée fin octobre 2005 ... c'est plus exitant que le HTML!

Administrateur PHPfrance
Administrateur PHPfrance | 3088 Messages

08 nov. 2007, 09:52

J'ai un peu lu le sujet, mais il y avait beaucoup de mots donc je vous propose une version alternative.

Concernant GROUP BY, avant toute chose il faut pouvoir se représenter le résultat d'une requête non pas sous la forme d'un tableau comme c'est communément le cas, mais plutôt sous la forme d'un gros camembert (un ensemble). GROUP BY sert à tracer des droites pour découper ce camembert où chaque portion représente un groupe.

Par exemple, si ton camembert représente les résultat d'un coureur sur toute sa carrière, tu vas sûrement vouloir le découper par année, chaque portion représentera les résultat de chaque année. Sauf qu'ici, d'après ce que j'ai compris le camembert représente les résultat d'une seule année, donc il est inutile de le redécouper. Autrement dit, pas de GROUP BY.

À part ça, concernant la mise en forme de tes messages, évite de présenter tes requêtes sous forme de PHP, ça n'aide pas vraiment la lecture. Contente-toi d'afficher la requête telle qu'elle est exécutée par le serveur, sans PHP. Je vois aussi que tu coupes ta chaîne de caractère sur plusieurs lignes, ce n'est pas nécessaire et ça empêche le lecteur de faire un simple copier/coller. Tu peux laisser une chaîne ouverte tout en sautant des lignes, ça ne pose pas de problème.
$sql = 'SELECT ...
        FROM ...
        WHERE ...';
Concernant la requête, quelques recommandations : préfère LIKE à DATE_FORMAT(), ne mets pas de nombres entre guillemets si la colonne est de type *INT, ajoute un index sur course.date.

Finalement, ta requête devrait ressembler à

Code : Tout sélectionner

SELECT SUM(clas.point) as somme FROM #__classement AS clas INNER JOIN #__classement_course AS course ON course.id = clas.course_id WHERE clas.dossard = 195 AND course.date LIKE '2004-%' AND course.pat_chal = 'C'

Mammouth du PHP | 545 Messages

09 nov. 2007, 09:31

Grâce à votre aide, j'y suis enfin arrivée. Voici ma fonction me permettant de calculer les points d'un classement général:
function points ($annee, $dossard) {
	global $database;
	$countch = 10;

	$query = "SELECT SUM(clas.point) as somme, clas.dossard"    
		. "\n FROM #__classement AS clas"
    	        . "\n INNER JOIN #__classement_course AS course ON course.id = clas.course_id"
		. "\n WHERE course.date LIKE '" . (int)$annee . "-%' AND clas.dossard = '" . (int)$dossard . "' AND course.pat_chal = 'C'"
		. "\n GROUP BY clas.dossard"
		. "\n ORDER BY clas.point DESC"
		. "\n LIMIT $countch"
		;
	$database->setQuery( $query );
	$pointchal = $database -> loadResult();

	return $pointchal;
	}
Hubert Roksor,

Tu peux voir que j'ai un peu tenu compte de ton avis (like) néanmoins, je tiens à mes guillemets car j'y vois plus clair ... je ne suis pas programmeur à la base et je suis habitué à cette vision !
Malgré tout, je retiens ta suggestion pour l'avenir ... un jour, je vais retravailler la qualité de mon composant !

Merci

NB: Maintenant que j'ai mon tableau, je coince sur le tri ... à voir icisi vous avez le temps !
Sebe

Pour moi, le PHP est une nouvelle aventure qui a commencée fin octobre 2005 ... c'est plus exitant que le HTML!