Page 1 sur 1

Problème avec SUM

Posté : 07 nov. 2007, 10:37
par Sebe
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

Posté : 07 nov. 2007, 10:47
par zeus
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 ? ;)

Posté : 07 nov. 2007, 12:08
par Sebe
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

Posté : 07 nov. 2007, 12:19
par zeus
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)

Posté : 07 nov. 2007, 12:29
par Ryle
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 :)

Posté : 07 nov. 2007, 13:09
par Sebe
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

Posté : 07 nov. 2007, 13:44
par Sebe
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é !

Posté : 07 nov. 2007, 13:45
par zeus
Affiche tes messages d'erreur ;)

il ne faut pas de AS dans un GROUP BY

Posté : 07 nov. 2007, 15:08
par Sebe
Affiche tes messages d'erreur ;)
Je n'en ai pas !
il ne faut pas de AS dans un GROUP BY
Ok, enlevé !

Posté : 07 nov. 2007, 16:52
par Ryle
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

Posté : 08 nov. 2007, 00:07
par Sebe
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

Posté : 08 nov. 2007, 09:52
par Hubert Roksor
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'

Posté : 09 nov. 2007, 09:31
par Sebe
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 !