Plusieurs jointures sur la meme table [complexe]

Wells
Invité n'ayant pas de compte PHPfrance

05 juin 2006, 20:56

Bonjour a tous, comme ma question est un poil compliqué, je vas prendre pour l'illustré l'exemple de la coupe du monde de foot.

Voila mes tables:

Code : Tout sélectionner

table_pays: `id` VARCHAR( 2 ) NOT NULL , <- id du pays (exemple: A1, groupe A, equipe 1) `nom` VARCHAR( 255 ) NOT NULL , <- nom du pays `groupe` VARCHAR( 1 ) NOT NULL , <- groupe du pays

Code : Tout sélectionner

table_match: `id` int(3) NOT NULL auto_increment,<- id unique (on s'en occupe pas) `j1` char(2) NOT NULL default '',<- id de l'equipe 1 `j2` char(2) NOT NULL default '',<- id de l'equipe 2 `gagnant` char(2) NOT NULL default '0',<- id du gagnant ou E pour match nul `s1` int(2) NOT NULL default '0',<- score equipe 1 `s2` int(2) NOT NULL default '0',<- score equipe 2
Voila un modele de BDD classique. Bien maintenant le but de la manoeuvre est de faire le classement des equipes par points.
Petit rappel de regles:
gagner un match donne 2 pts
faire match nul donne 1 pts

Voila la requete que j'utilise:

SELECT ((COUNT(t1.id)*2)+COUNT(t2.id)) AS pts

FROM table_pays AS p

LEFT JOIN table_match AS t1 ON t1.gagnant=p.id

LEFT JOIN table_match AS t2 ON (t2.j1=p.id OR t2.j2=p.id) AND gagnant='E'

WHERE groupe='A'

GROUP BY 'p.id'

ORDER BY 'pts'


Malheureusement vous l'aurez compris ca ne marche pas. (sinon qu'est ce que je ferais la?? ^^)

En fait la deuxieme jointure (COUNT(t2.id)) me retourne le double d'enregitrement que la vérité.

Donc je pense que plusieurs jointure sur une meme table ce fait avec une autre syntaxe.

Si des pros passe par la, vous me sortiriez une épigne du pieds :)

Wells

Eléphant du PHP | 140 Messages

06 juin 2006, 10:47

Je te conseille tres fortement la lecture de la documentation SQL de ta base de données.
COUNT (t1.id) ou COUNT(t2.id) renvoient exactement la meme valeur... a savoir le nombre d'enregistrements non-nulls renvoyes par ta requete (clause WHERE et non JOIN).

Pour arriver au resultat que tu souhaites, il te faut soit faire plusieurs requetes, soit bidouiller.
Quand je parle de bidouille, je parle d'utiliser une formule fournissant le nombre de points a chaque match utilisant des IF par exemple.

Eléphant du PHP | 113 Messages

06 juin 2006, 12:12

Hum pas d'accord, les deux COUNT ne retourne pas la meme valeur. Le premier COUNT retourne le bon résultat (celui de ma requete du JOIN), le second retourne le double de résultat (de ma seconde requete de JOIN).

Je pense que c'est du a une mauvaise jointure, pourtant je met des LEFT.

Quoi qu'il en soit, je confirme a 100% que les deux count ne retourne pas la meme valeur ;)

Eléphant du PHP | 140 Messages

06 juin 2006, 13:31

Hum pas d'accord, les deux COUNT ne retourne pas la meme valeur. Le premier COUNT retourne le bon résultat (celui de ma requete du JOIN), le second retourne le double de résultat (de ma seconde requete de JOIN).
Oui effectivement... j'avais cru qu'il s'agissait de la meme table.

Maintenant ma reponse reste valable, tu ne peux pas obtenir le resultat que tu souhaites de cette maniere.
En effet, ce que ta requete fait c'est :
- a chaque pays
- elle essaie d'associer un match ou le pays a gagne
- elle essaie d'associer un match ou le pays a fait match nul
Il est donc evident que cela ne correspond pas a ce que tu recherches.

Je peux te garantir que ce n'est pas la bonne maniere pour proceder.

Eléphant du PHP | 113 Messages

06 juin 2006, 14:42

En passant par des sous requetes ce n'est pas possible?
Je dit ca car la méthode que je présente ci dessus fonctionne sur un serveur SQL. alors certe il y a des différences avec MySQL mais j'aurais cru que sa fonctionner.

Quoi qu'il en soit l'interet de calculer tout ca par une requete est de pouvoir faire un tri direct, donc gain de temps.
Sinon il me faut passer par plusieurs requetes et des tableaux -> lourd.

Eléphant du PHP | 140 Messages

06 juin 2006, 15:18

En passant par des sous requetes ce n'est pas possible?.
Si. Mais fais bien attention au support des sous-requetes de MySQL ;-)
Quoi qu'il en soit l'interet de calculer tout ca par une requete est de pouvoir faire un tri direct, donc gain de temps.
Sinon il me faut passer par plusieurs requetes et des tableaux -> lourd.
En fait, la logique serait :
- soit de stocker le nombre de points en base de donnees
- soit de faire une procedure stockee

Eléphant du PHP | 113 Messages

06 juin 2006, 15:26

Vi mais stocké c lourd aussi, impossible. La procédure stockée ne change rien au soucis, puisqu'il faut quand meme la requete qu iretourne les points :)

Par contre je maitrise pas les sous requetes si qq'un peu m'expliquer en reprendant mon exemple gracias!! :)

Wells

Eléphant du PHP | 140 Messages

06 juin 2006, 16:38

Vi mais stocké c lourd aussi, impossible. La procédure stockée ne change rien au soucis, puisqu'il faut quand meme la requete qu iretourne les points :)
Non, je pense que tu ne sais pas ce que c'est qu'une procedure stockee. Il s'agit d'un ensemble de requetes stockees cote serveur et qui justement accelerent ton code en evitant les aller retour avec le client.
Par contre je maitrise pas les sous requetes si qq'un peu m'expliquer en reprendant mon exemple gracias!! :)
C'est tres simple, c'est du style

Code : Tout sélectionner

SELECT * FROM table1 t1 where t1.ide IN SELECT id from table2;
Mais si tu es sur MySQL, tu risques d'avoir un certain nombre de limitations tres bloquantes de ce cote-la.

Pour conclure, si tu es sous MySQL et que tu n'as pas une version te permettant de faire des procedures stockees tu as 2 solutions :
- soit tu fais plusieurs requetes
- soit tu dois stocker le nombre de points de chaque equipe dans une table (solution la plus logique)

Eléphant du PHP | 91 Messages

07 juin 2006, 23:54

Une solution tout SQL simple

Code : Tout sélectionner

create [temporary] table table_point `id` VARCHAR( 2 ) NOT NULL , <- id du pays (exemple: A1, groupe A, equipe 1) `point` int NOT NULL , <- groupe du pays Insert table_point (id,point) Select j1 , count(*)*2 From table_match Where s1>s2 Group By j1 Insert table_point (id,point) Select j1 , count(*) From table_match Where s1=s2 Group By j1 Insert table_point (id,point) Select j2 , count(*)*2 From table_match Where s1<s2 Group By j2 Insert table_point (id,point) Select j2 , count(*) From table_match Where s1=s2 Group By j2 Select id , sum(point) as score From table_point Group By id Order by score

Eléphant du PHP | 140 Messages

08 juin 2006, 09:07

Une solution tout SQL simple
...
Oui, tout a fait. Ca consiste a faire ce que je preconisais plus haut : stocker les points en base (ici de maniere temporaire).

Eléphant du PHP | 113 Messages

08 juin 2006, 09:19

Heu toute simple toute simple. Pas vraiment, meme si c'est du remporaire, ca reste une multiplication des requetes, donc une diminution des performances.

Je suis sur que y a une facon de faire ca en une seule et unique requete (oui car la j'ai simplifier le probleme, mais normalement aprés le COUNT sur les points, je doit recuperer le n de but marqué et encaissé pour calculer le goal avarage)

Donc peu etre du coté des sous-requetes mais je suis pas assez callé pour pondre un code ^^

Wells

Eléphant du PHP | 140 Messages

08 juin 2006, 10:47

Je suis sur que y a une facon de faire ca en une seule et unique requete (oui car la j'ai simplifier le probleme, mais normalement aprés le COUNT sur les points, je doit recuperer le n de but marqué et encaissé pour calculer le goal avarage)
Donc peu etre du coté des sous-requetes mais je suis pas assez callé pour pondre un code ^^
Comme je te l'ai expliqué plus haut, si tu utilises une version standard de MySQL, la reponse est "non" (a moins qu'il y ait eu d'enormes evolutions depuis la 4.0 mais a ma connaissance ce n'est pas le cas).

La raison en est tres simple : par definition une requete consiste a renvoyer un set de données disponibles dans la table. Or ici, tu souhaites d'abord produire un premier set de données (pour calculer le nombre de points) avant de pouvoir sortir un 2eme set.
C'est quelque chose qui n'est pas faisable en MySQL.

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 13231 Messages

08 juin 2006, 10:50

(a moins qu'il y ait eu d'enormes evolutions depuis la 4.0 mais a ma connaissance ce n'est pas le cas).

La raison en est tres simple : par definition une requete consiste a renvoyer un set de données disponibles dans la table. Or ici, tu souhaites d'abord produire un premier set de données (pour calculer le nombre de points) avant de pouvoir sortir un 2eme set.
Avec MySQL 4.1, il y a eu l'introduction des sous-requetes ;)

Je n'ai pas lu le post en entier mais est-ce que ça peut répondre au problème ?
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

Eléphant du PHP | 140 Messages

08 juin 2006, 11:29

Avec MySQL 4.1, il y a eu l'introduction des sous-requetes ;)
Introduction partielle malheureusement :? Je ne crois pas que MySQL supporte les requetes corellees...
Je n'ai pas lu le post en entier mais est-ce que ça peut répondre au problème ?
Je n'en suis pas certain. Plus j'y reflechis, moins je pense que c'est faisable en une seule requete.

Eléphant du PHP | 113 Messages

09 juin 2006, 10:02

Alors s'en m'avance, il me semble que MySQL 5.0 a eu une grosse évolution.

Donc si qq'un si connais dans la salle ou a deja fait ce genre de choses :)

Wells