(Réglé) Jointure problématique ?

Jointure problématique ? (Réglé) - SQL/NoSQL - Programmation

Marsh Posté le 30-07-2004 à 12:18:23    

Bonjour,
 
J'ai un problème (Non ? C'est vrai !?) avec une requête SQL (support : PHP, donc du mySQL). Pour placer le contexte, il s'agit d'un jeu de pari.  
Voici une partie de la structure des tables concernées (pas besoin de donner tous les champs, vu qu'ils n'interviennent pas tous) :
 
tw_player (stockage des joueurs)
id (smallint, auto_increment, primaire)
login (smallint, index. Permet de pointer vers l'id du membre dans la table communaute)
cash (smallint. Représente l'argent disponible pour le joueur)
 
tw_mises (stockage des mises)
mise (smallint. Représente l'argent misé sur le choix x)
joueur (smallint, index. Pointe sur le champ id de tw_player)
 
communaute (stockage ds membres)
id (smallint, primaire, auto_increment)
login (varchar(32). Stocke le login du membre)
 
Ce que je souhaite faire :  
Un classement des richesses cumulées, c'est à dire du capital disponible (tw_player.cash) ajouté au total des mises en cours (donc "mise" de tw_mises).  
 
Ce que j'arrive à faire :
Un classement des richesses cumulées, comme indiqué, mais UNIQUEMENT pour les personnes ayant au minimum une mise de faite. C'est à dire que les personnes qui ont un capital disponible mais aucune mise ne sont pas reprises dans le classement.
 
Je suppose que cela vient de la jointure, ayant appris SQL sur le tas en autodidacte (etudiant powah), je n'ai sans doute pas les nuances exactes des jointures. Mais ni left, ni right, ni inner ne corresponde à ce que je veux obtenir (ça ne fait qu'empirer la situation en retirant l'une ou l'autre entrée dans le classement, je n'ai pas pu voir selon quel critère).
 
select (sum(tw_mises.mise)+tw_player.cash) as critere, tw_player.login as login, communaute.login as name FROM tw_player LEFT JOIN tw_mises ON tw_mises.joueur=tw_player.id LEFT JOIN communaute ON communaute.id=tw_player.login GROUP BY tw_mises.joueur ORDER BY critere DESC
 
J'espère qu'une âme charitable saura m'aider, et surtout m'expliquer ce qui ne va pas dans ma requête :) (Le principal étant de comprendre ! Je me fous totalement du fait que ça fonctionne si je n'ai pas compris ^^).  
 
Merci d'avance :)


Message édité par guybrush02 le 02-08-2004 à 20:03:48

---------------
Guybrush  
Reply

Marsh Posté le 30-07-2004 à 12:18:23   

Reply

Marsh Posté le 30-07-2004 à 12:22:25    

Et tant qu'à abuser du temps de certains, quelle est la différence fondamentale entre une jointure de type * JOIN et un WHERE table1.champ1=table2.champ1 ?  
 
Cette deuxième écriture correspond-elle à une INNER JOIN ou je me plante complètement ?
 
Merci


---------------
Guybrush  
Reply

Marsh Posté le 30-07-2004 à 14:28:24    

il y a peut etre un différence profonde mais elle ne saute pas au zyeux...
 
sinon le problème que tu as c peut etre que tu utilise des INNER JOIN justement. une requete inner join ne retourne que les lignes des deux tables pour lesquelles les champs joints sont égaux cad que si tu as une ligne A dans ta première table mais pas de ligne lui correspondant dans la deuxième la ligne A ne sera pas retournée...
 
pour cela il faut utliser des jointures a gauche ou a droite (LEFT JOIN ou RIGHT JOIN) qui retourne toutes les lignes d'une table et seulement celles de l'autre table qui ont les champs joints égaux
 
voili voilo


Message édité par tibob26 le 30-07-2004 à 14:29:29

---------------
patience et longueur de temps...
Reply

Marsh Posté le 30-07-2004 à 15:45:20    

tibob26 a écrit :


pour cela il faut utliser des jointures a gauche ou a droite (LEFT JOIN ou RIGHT JOIN) qui retourne toutes les lignes d'une table et seulement celles de l'autre table qui ont les champs joints égaux


 
Actuellement, j'utilise des LEFT JOIN, ce qui me semblait le plus adéquat (on prend les joueurs, et on y ajoute la table des mises si y a une correspondance) mais ça ne donne pas le résultat escompté, vu qu'il ne va pas me prendre les joueurs n'ayant pas de mises...  
 
En right join ou inner join, ça ne change rien, le comportement varie un peu mais ne donne pas le résultat souhaité (il y a des enregistrements en moins lors de la lecture, curieusement, et je n'ai pas pu trouver le "point commun" entre ces enregistrements... ils sont 1 ou 2 maximum.)


---------------
Guybrush  
Reply

Marsh Posté le 30-07-2004 à 16:01:43    

Code :
  1. select tw_player.id, tw_player.cash, tw_mises.mise from tw_player left join tw_mises on tw_player.id = tw_mise.joueur


 
te retourne normalement tous les joueurs et leur cash ansi que leur mise s'ils en ont sinon je n'y comprend plus rien, ou alors c'est right join (je les confond toujours les 2)
 
sinon c normal qu'il n'y ait pas d'identifiant a ta table tw_mises...?


---------------
patience et longueur de temps...
Reply

Marsh Posté le 30-07-2004 à 17:49:16    

tibob26 a écrit :


 
te retourne normalement tous les joueurs et leur cash ansi que leur mise s'ils en ont sinon je n'y comprend plus rien, ou alors c'est right join (je les confond toujours les 2)
 
sinon c normal qu'il n'y ait pas d'identifiant a ta table tw_mises...?


 
Moi non plus je ne comprends pas pourquoi ça ne renvoit pas le résultat souhaité :) Je n'ai que quelques bases en SQL, je suis "free-lance" pour l'apprentissage, mes connaissances ont des limites. Mais c'est une requête somme toute "assez simple", et elle ne fonctionne pas parfaitement.  
 
En gros, y a 74 joueurs, et la requête n'en retourne que 63. Ceux qui sont dans les 11 absents sont les joueurs n'ayant aucune mise en cours.  
 
Quant à l'absence d'identifiant pour la table tw_mises, c'est parce que je n'ai pas mis les structures complètes, il y a bel et bien un identifiant pour les mises (en plus d'un index sur les choix disponibles, et un champ de performance pour rapidement récupérer les mises concernant un événement. Il y a également un datetime pour la chronologie des votes à la seconde près.)


---------------
Guybrush  
Reply

Marsh Posté le 30-07-2004 à 23:11:31    

Juste pour info, normalement, dans un LEFT JOIN, il faut préciser le OUTER "LEFT OUTER JOIN" car rien n'empêche le SGBD de faire un LEFT INNER JOIN", ce qui reviens à un RIGHT JOIN (qui lui ne peut être que INNER d'après mes souvenirs de cours)
 
Ceci-dis cette syntaxe est monstrueusement pourrie à mon goût et particulièrement imbittable. Vive les syntaxes de SQL Server et Oracle sur ces points, qui sont quant à elles parfaitement lisibles :)

Reply

Marsh Posté le 31-07-2004 à 00:25:19    

Arjuna a écrit :

Juste pour info, normalement, dans un LEFT JOIN, il faut préciser le OUTER "LEFT OUTER JOIN" car rien n'empêche le SGBD de faire un LEFT INNER JOIN", ce qui reviens à un RIGHT JOIN (qui lui ne peut être que INNER d'après mes souvenirs de cours)


 
Je n'ai malheureusement que deux mauvaises nouvelles :
 - La précision "OUTER" ne semble pas faire comprendre à mySQL qu'il doit prendre chaque "player" et (si elle existe) la somme des mises.
 - Je n'aurais pas la chance d'avoir un cours de SGBD avant encore environ 50 jours (Vive les vacances ?)... :'(


---------------
Guybrush  
Reply

Marsh Posté le 01-08-2004 à 22:50:45    

Après quelques tests, le problème semble venir de la fonction sum() ou du GROUP BY.
 
En gros, si je sélectionne UNE mise correspondant au joueur, soit il me la met, soit il prend NULL (dans le cas ou y a aucune mise en cours). Mais quand il prend de la sorte, il m'affiche bien la totalité des entrées.
 
 
Par contre, si je fais un select sum(NULL), j'obtiens NULL.
Donc si on suit cette logique, le résultat renvoyé par sum(tw_mises.mise) (ou tw_mises.mise n'existe pas, donc vaut NULL) devrait être NULL (ce qui n'est pas le cas dans la pratique).  
 
Si je procède ainsi, j'obtiens 83 résultats. Si je précise le group by (à cause d'un count, d'un sum, etc...) je descends à 56, soit le nbr de personnes ayant des mises en jeu pour l'instant sauf... (voir ci-dessous ^^)
 
MAIS, petite nouveauté : Le joueur "Koubiak" n'a pas de mise ET il apparait dans les résultats... Il n'a pourtant rien de plus qu'un autre au niveau de l'entrée. Bref, je reste perplexe. (Surtout qu'un select sum(mise) from tw_mises group by joueur me renvoit 55 résultats ^^)
 


---------------
Guybrush  
Reply

Marsh Posté le 01-08-2004 à 22:54:15    

A peine ai-je posté le message que je viens de tester autre chose qui semble fonctionner.  
 
Au lieu de la requête suivante :
 
select (sum(tw_mises.mise)+tw_player.cash) as critere, tw_player.login as login, communaute.login as name FROM tw_player LEFT JOIN tw_mises ON tw_mises.joueur=tw_player.id LEFT JOIN communaute ON communaute.id=tw_player.login GROUP BY tw_mises.joueur ORDER BY critere DESC
 
Je mets
 
select (sum(tw_mises.mise)+tw_player.cash) as critere, tw_player.login as login, communaute.login as name FROM tw_player LEFT JOIN tw_mises ON tw_mises.joueur=tw_player.id LEFT JOIN communaute ON communaute.id=tw_player.login GROUP BY tw_player.id ORDER BY critere DESC
 
La seule différence, comme vous le voyez, est la clause utilisée pour le group by. Elle est d'ailleurs stupide dans la 2e version, vu que le champ id est unique, donc un group by suivant un champ unique n'est pas très malin. Pourtant, ça marche mieux (il renvoit les 83 résultats). A vrai dire, ça marche "comme il faut". Je suppose que cette "particularité" doit provenir de la jointure ?
 
J'avoue que je reste perplexe quant à ce problème rencontré. Mes connaissances étant limitées dans le domaine, j'espère que si quelqu'un rencontre le même problème, il comprendra mieux que moi :)


---------------
Guybrush  
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed