sum avec left join

Eléphant du PHP | 94 Messages

23 sept. 2006, 22:03

Bonjour

Je rencontre un phénomène étrange avec une requete.

je fais une requete ou j'associe deux tables.
lorsque je fais une somme avec une valeur, j'obtiens un resultat différent selon si l'active ou non la jointure (le résultat étant juste quand la jointure n'est pas activée). Cela vient il de ma requete ?

Ce qui fonctionne :
	$reqY =  "select jour, sum(valeur) as valeur"
				. " from table"
				. " where YEAR(jour)='2006"
				. " group by YEAR(jour) ";
Ce qui ne fonctionne pas (ou en tout cas qui me donne un résultat faux):
	$reqY =  "select jour, sum(valeur) as valeur,table2.jour as jour2"
				. " from table"
				. " LEFT JOIN table2 ON table.jour=table2.jour"
				. " where YEAR(jour)='2006'"
				. " group by YEAR(jour) ";
rspir

Modérateur PHPfrance
Modérateur PHPfrance | 7636 Messages

23 sept. 2006, 23:00

Il faudrait peut être un jeu de données histoire de se faire une idée.
Ce que tu as et ce que tu veux obtenir.

Mais le résultat peut varier s'il n'y a pas de relation entre les jours de la table 1 et la table 2.

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

Eléphant du PHP | 94 Messages

23 sept. 2006, 23:34

Voici les tables

Code : Tout sélectionner

CREATE TABLE `table1` ( `jour` date NOT NULL, `valeur` int(6) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1; INSERT INTO `table1` VALUES ('2006-09-11', 3928); INSERT INTO `table1` VALUES ('2006-09-12', 6497); INSERT INTO `table1` VALUES ('2006-09-13', 6303); INSERT INTO `table1` VALUES ('2006-09-14', 7541); INSERT INTO `table1` VALUES ('2006-09-15', 8570); INSERT INTO `table1` VALUES ('2006-09-16', 1126); CREATE TABLE `table2` ( `n` int(5) NOT NULL auto_increment, `jour` date NOT NULL default '0000-00-00', `c1` int(5) NOT NULL default '0', PRIMARY KEY (`n`), KEY `date` (`jour`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=2701 ; INSERT INTO `table2` VALUES (2691, '2006-09-11', 0); INSERT INTO `table2` VALUES (2692, '2006-09-11', 252); INSERT INTO `table2` VALUES (2693, '2006-09-11', 0); INSERT INTO `table2` VALUES (2694, '2006-09-11', 11); INSERT INTO `table2` VALUES (2695, '2006-09-11', 0); INSERT INTO `table2` VALUES (2696, '2006-09-12', 229); INSERT INTO `table2` VALUES (2697, '2006-09-12', 0); INSERT INTO `table2` VALUES (2698, '2006-09-12', 39); INSERT INTO `table2` VALUES (2699, '2006-09-12', 8); INSERT INTO `table2` VALUES (2700, '2006-09-12', 0);
Voici le code ... il faut juste adapter pour accéder à la bdd
Le resultat renvoyé est différent selon si on active le join
<?php

// ACCES MYSQL
	include("../../inc/constant.php"); 
	$connexion = connexion(user, pass, bdd, server); 

// Récupération de la dernière date 
	$reqdate="select DATE_FORMAT(jour, '%d/%m/%Y') AS 'date_fr',jour from table2 order by jour desc limit 1";
	$resdate = ExecRequete ($reqdate, $connexion);
	$datecamp = ObjetSuivant ($resdate);
	$dat_aff=$datecamp->date_fr;
	
// Extraction du N° de semaine et de l'année		
	 $sEngDate = substr ($dat_aff, -4).substr ($dat_aff, 3, 2).substr ($dat_aff, 0, 2);
     $iTime = strtotime ($sEngDate);
     $semfiltre=date ('W', $iTime); 
     $anfiltre=date ('Y', $iTime); 
	
	$req =  "select table1.jour, sum(valeur) as valeur"
				. " FROM table1"
		//		. " LEFT JOIN table2 ON table1.jour=table2.jour"
				. " where YEAR(table1.jour) = '$anfiltre'"
				. " group by WEEK(table1.jour) ";
			
	$res = ExecRequete ($req, $connexion);
	$obj_w=0;						
	$obj_y=0;			
	
		while ($prod_w = LigneSuivante ($res))
		{
			if ($prod_w[objweek]==$semfiltre)
			{
				$obj_w = $prod_w[valeur]  ;
			}
			$obj_y = $prod_w[valeur] +$obj_y ;
		}
echo $obj_y;
?>
rspir

Modérateur PHPfrance
Modérateur PHPfrance | 7636 Messages

24 sept. 2006, 00:52

La somme est effectuée pour chaque couple "table.jour=table2.jour" trouvé.

Il y a 5 correspondances sur les deux tables entre "2006-09-11" et "2006-09-12" ce qui a pour effet de sortir les tuples :

2006-09-11', 3928 -- 2691, '2006-09-11', 0
2006-09-11', 3928 -- 2692, '2006-09-11', 252
2006-09-11', 3928 -- 2693, '2006-09-11', 0
2006-09-11', 3928 -- 2694, '2006-09-11', 11
2006-09-11', 3928 -- 2695, '2006-09-11', 0

De même pour "2006-09-12" et pour finir les autres valeur sont également ajoutées puisque le LEFT JOIN prend toutes les valeurs de la table1.

Pour vérifier tu peux exécuter la requête sans le calcul :
<?php $reqY =  "select jour,table2.jour as jour2"
                . " from table"
                . " LEFT JOIN table2 ON table.jour=table2.jour"
                . " where YEAR(jour)='2006'";

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

Eléphant du PHP | 94 Messages

24 sept. 2006, 09:25

Ah oui effectivement....
Y'a til un moyen pour faire la somme de la valeur de la table1 :

-> même s'il n'y a pas de correspondance dans la table2
-> en ne comptant qu'une fois la valeur (dans le cas ou la table2 a plusieurs correspondances ?)
rspir

Mammouth du PHP | 19672 Messages

24 sept. 2006, 09:42

J'ai mis un moment avant de comprendre ce que tu essayes de faire. EN fait, tu veux la somme des valeurs pour chaque date de la table 1 qu'on trouve dans la table 2 : si c'est bien ça et que ta version de MySQL te permet les sous-requête, voici une proposition :

Code : Tout sélectionner

SELECT t1.jour, SUM(t1.valeur) AS valeur FROM table1 AS t1 WHERE t1.jour IN ( SELECT DISTINCT(t2.jour) FROM table2 AS t2 ) AND YEAR(t1.jour) = 2006 GROUP BY t1.jour;
Et sur la base du jeu d'essai que tu as donné plus haut, ça retourne ceci:

Code : Tout sélectionner

mysql> SELECT t1.jour, SUM(t1.valeur) AS valeur -> FROM table1 AS t1 -> WHERE t1.jour IN ( -> SELECT DISTINCT(t2.jour) -> FROM table2 AS t2 -> ) -> AND YEAR(t1.jour) = 2006 -> GROUP BY t1.jour; +------------+--------+ | jour | valeur | +------------+--------+ | 2006-09-11 | 3928 | | 2006-09-12 | 6497 | +------------+--------+ 2 rows in set (0.01 sec)
Partant du principe que le jour doit être le même dans les deux tables pour chaque ligne de résultat, il est inutile de sortir le jour de la table 2 dans le SELECT puisque tu l'as déjà avec le jour de la table 1.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Eléphant du PHP | 94 Messages

24 sept. 2006, 09:49

Bonjour Cyrano
EN fait, tu veux la somme des valeurs pour chaque date de la table 1 qu'on trouve dans la table 2
Non ma demande est moins restrictive.

Je veux avoir la somme des valeurs de la table1 (pour une période identifiée) qu'il soit dans la table2 ou non. Et s'ils sont dans la table2, qu'ils ne soient pas sommés plusieurs fois si plusieurs couples.

En fait j'ai besoin de la table2 pour rapatrier une donnée (si le couple existe).

Si ma demande n'est pas réalisable, je ferais deux requetes une sur chaque table avec le filtre de la période. Et je construierais des tableaux sous php pour recréer les couples mais j'espérais tout faire en une seule opération avec une requete.
rspir

Mammouth du PHP | 19672 Messages

24 sept. 2006, 09:55

Bon, je capte pas trop : par rapport à ton jeu d'essai, quel résultat attends-tu au juste ? :-k
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Eléphant du PHP | 94 Messages

24 sept. 2006, 10:02

C'est vrai ... on aurait pu commencer par là :D

Bon, je voudrais avoir : 33965 et pas 75665
rspir

Mammouth du PHP | 19672 Messages

24 sept. 2006, 10:11

Tu pourrais détailler un peu ? ça ne m'avance pas des masse ta réponse : à quoi correspond ce résultat ? Ok, tu vas me dire que c'est la somme des valeurs qu'on a dans le jeu d'essai pour la table 1 : c'est insuffisant comme explication. Comment doit-on l'obtenir par rapport à la table 2 ? C'est ça la question. Essaye d'expliquer précisément selon quel critère on doit obtenir cette valeur.

Parce que sinon, on va simplifier :

Code : Tout sélectionner

mysql> SELECT SUM(valeur) AS total FROM table1; +-------+ | total | +-------+ | 33965 | +-------+ 1 row in set (0.01 sec)
Et ce n'est je suppose pas ce que tu cherches ?
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Eléphant du PHP | 94 Messages

24 sept. 2006, 10:30

ok
alors, soit une semaine du lundi au samedi. Donc 6 valeurs.
Je fais des prévisions dans ma table 1 pour chaque jour.
Et j'ai le resultat de chaque jour dans ma table 2.

Ce que je veux c'est obtenir avec une requete la somme des valeurs prévisionnelles (table1) et des valeurs réalisées (table2).

Quand je suis en milieu de semaine, je dois avoir la somme de toutes mes prévis du lundi au samedi et la somme du réalisé. Cela me permet de calculer le reste à faire et une couverture des prévisions.

Dans la table1, je vais avoir une valeur par date alors que dans la table 2, je vais avoir plusieurs enregistrements pour une même date (d'ou mes problèmes avec le left join qui génère plusieurs couples)

J'utilise ces résultats pour faire des graph avec jp graph et je veux limiter le nb de requete.

C'est plus clair ?
rspir

Mammouth du PHP | 19672 Messages

24 sept. 2006, 10:53

C'est plus clair ?
Non : soit ton jeu d'essai est incomplet, soit ton explication vaut pas cher : je t'ai mis les résultats dans un tableau clair et visuel, ben fais-moi la même chose avec ce que tu veux obtenir en expliquant à quoi correspond chaque valeur. Ajoute également dans le jeu d'essai des lignes de valeurs qui ne doivent pas être prises en compte pour compléter le test, par exemple des lignes pour des dates largement antérieures, l'an dernier ou ce que tu veux, des lignes dont les valeurs ne doivent pas sortir.

J'ajoute qu'on fait pas de prévisionnel en SQL : on calcule pour obtenir des résultats bruts sur la base de valeurs existantes, pas sur des valeurs supposées. Et la colonne valeur de la table 1 correspond à quoi dans la table 2 ?
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Eléphant du PHP | 94 Messages

24 sept. 2006, 11:18

ok je crois effectivement que c'est necessaire.
il me faut un peu de temps. Je tacherais de le faire dans la journée.

cependant, juste un détail, quand je parle de prévisions : je ne demande pas à mysql de faire des prévisions. Je me suis en effet mal exprimé.
Ce sont des prévisions que je fais par ailleurs et que je stocke dans la table.

Donc en début de semaine, j'ai ma table1 avec 6 valeurs (une pour chaque jour). Et au fil de la semaine la table2 "réalisé" se remplit.
rspir

Mammouth du PHP | 19672 Messages

24 sept. 2006, 12:11

Fais des tableaux dans le bloc-note (police à chasse fixe pour que ça soit lisible) :

-1- les données existantes :

Code : Tout sélectionner

mysql> SELECT * FROM table1; +------------+--------+ | jour | valeur | +------------+--------+ | 2006-09-11 | 3928 | | 2006-09-12 | 6497 | | 2006-09-13 | 6303 | | 2006-09-14 | 7541 | | 2006-09-15 | 8570 | | 2006-09-16 | 1126 | +------------+--------+ 6 rows in set (0.00 sec) mysql> SELECT * FROM table2; +----+------------+-----+ | n | jour | c1 | +----+------------+-----+ | 1 | 2006-09-11 | 0 | | 2 | 2006-09-11 | 252 | | 3 | 2006-09-11 | 0 | | 4 | 2006-09-11 | 11 | | 5 | 2006-09-11 | 0 | | 6 | 2006-09-12 | 229 | | 7 | 2006-09-12 | 0 | | 8 | 2006-09-12 | 39 | | 9 | 2006-09-12 | 8 | | 10 | 2006-09-12 | 0 | +----+------------+-----+ 10 rows in set (0.00 sec)
-2- Ce que tu veux obtenir :

Code : Tout sélectionner

+------------+--------+------------+ | jour | valeur | jour2 | +------------+--------+------------+ | ?????????? | ????? | ?????????? | +------------+--------+------------+
À partir de là on pourra d'abord comprendre et ensuite construire la requête appropriée pour autant que les explications sur ce qui correspond à quoi soient également présentes.

Alberrt Einstein disait : "Un problème non résolu est un problème mal posé" ;)
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Eléphant du PHP | 94 Messages

24 sept. 2006, 15:08

Bon j'ai enrichi l'exemple.
Voila la 1ère table. Elle stocke des données prévisionnelle (calculées en amont de mysql)


table1

Code : Tout sélectionner

----------------------- | jour | valeur | ----------------------- | 2006-09-04 | 1000 | | 2006-09-05 | 2000 | | 2006-09-11 | 3928 | | 2006-09-12 | 6497 | | 2006-09-13 | 6303 | | 2006-09-14 | 7541 | | 2006-09-15 | 8570 | | 2006-09-16 | 1126 | ----------------------
La somme des valeurs pour la semaine du 11/09 est de 33965
La somme des valeurs pour le mois de septembre est de 36965

La table 2 est alimenté de résultat quotidien.
Il peut y'avoir plusieurs enregistrements pour une journée.
le champ c1 peut prendre la valeur 0
il peut y'avoir des journées dans résultat (donc pas d'enregistrement)

table2

Code : Tout sélectionner

------------------------- | n | jour | c1 | ------------------------- | 1 | 2006-09-04 | 0 | | 2 | 2006-09-04 | 10 | | 3 | 2006-09-04 | 10 | | 4 | 2006-09-04 | 10 | | 5 | 2006-09-04 | 0 | | 6 | 2006-09-05 | 20 | | 7 | 2006-09-05 | 0 | | 8 | 2006-09-05 | 20 | | 9 | 2006-09-05 | 20 | | 10 | 2006-09-05 | 0 | | 11 | 2006-09-11 | 0 | | 12 | 2006-09-11 | 252 | | 13 | 2006-09-11 | 0 | | 14 | 2006-09-11 | 11 | | 15 | 2006-09-11 | 0 | | 16 | 2006-09-12 | 229 | | 17 | 2006-09-12 | 0 | | 18 | 2006-09-12 | 39 | | 19 | 2006-09-12 | 8 | | 20 | 2006-09-12 | 0 | -------------------------
Pour la semaine du 11/09 la somme de C1 est de 539
Pour septembre le cumul de données est de 629

Ce que je veux faire au final ??? : un graphique dans jp graph qui montre à tout moment ce qui est prévu de faire et ou en est le réalisé.
Si on regarde en début de période, il peut ne pas y'avoir de realisé, mais le graphique doit se faire avec le previ

Le resultat final attendu est du type :

Code : Tout sélectionner

---------------------------------------- | periode | valeur | c1 | ---------------------------------------- | S37 | 33965 | 539 | | Septembre | 36965 | 629 | ----------------------------------------
La période est calculée en fonction du champ jour de la table2 : j'ai une requete qui regarde le dernier jour de réalisé et qui calcule la semaine de cette date et le mois. Donc ici on a la semaine 34 (semaine du 12/09/06) et le mois de septembre.
De la je calcule en php le restant à traiter. Je calcule également un % de couverture.

Le mois de septembre se fait par la somme de semaine calendaire et non de date à date)
Donc j'attends en sortie de requete un truc du style :

Code : Tout sélectionner

--------------------------- | periode| valeur | c1 | --------------------------- | S36 | 3000| 90 | | S37 | 33965| 539 | ---------------------------
rspir