[MYSQL] Problème de jointure

Fermé
wingover Messages postés 21 Date d'inscription lundi 15 décembre 2008 Statut Membre Dernière intervention 17 juin 2011 - 8 janv. 2009 à 18:39
 toto - 12 janv. 2009 à 15:40
Bonjour,

J'ai un petit problème en MySQL que je n'arrive pas à résoudre.

Prenons l'exemple suivant :

Des individus peuvent effectuer des actions envers d'autres individus(dont eux-mêmes).
Je prends 4 individus (pim, pam, poum et toto) référencés dans la table user:
table: user
-------------
| id | nom  |
-------------
|  1 | pim  |
|  2 | pam  |
|  3 | poum |
|  4 | toto |
-------------

Imaginons que toto effectue l'action "action1" et pim l'action "action2":
table: action
-------------------------
| id |  label  | auteur |
-------------------------
|  1 | action1 |   4    |
|  2 | action2 |   1    |
-------------------------

L'action "action1" s'appliquait à pim, pam et poum tandis que l'action "action2" s'appliquait à pam et toto:
table de liaison : distrib
-----------------------
| id_action | id_user |
-----------------------
|     1     |    1    |
|     1     |    2    |
|     1     |    3    |
|     2     |    2    |
|     2     |    4    |
-----------------------

Maintenant j'aimerais pouvoir récupérer par une unique requête MySQL les information concernant toto pour avoir ce résultat:
-------------------------------------------------------
|  label  |    role   |         commentaires          |
-------------------------------------------------------
| action1 |   auteur  | destinataires:  pim,pam,poum  |
| action2 | recepteur | auteur: pim                   |
-------------------------------------------------------

Je pensais pouvoir faire ça avec la reqûete suivante :
SELECT a.label,
       IF(a.auteur=4,"auteur", "recepteur") role,
       IF(a.auteur=4, CONCAT("destinataires:",GROUP_CONCAT(' ',d.id_user)), 
                            CONCAT("auteur: ",a.auteur) ) commentaires

FROM action a RIGHT OUTER JOIN distrib d ON d.id_action = a.id
              JOIN user ru ON ru.id = d.id_user
              JOIN user au ON au.id = a.auteur

WHERE a.auteur=4 OR d.id_user=4

GROUP BY d.id_action

Apparement non mais je pense ne pas être loin. Je m'emmêle juste un peu les pédales dans les jointures.
Si quelqu'un peu m'apporter un peu de lumière je lui en serait grandement reconnaissant.

Merci d'avance.

11 réponses

wingover Messages postés 21 Date d'inscription lundi 15 décembre 2008 Statut Membre Dernière intervention 17 juin 2011
9 janv. 2009 à 13:11
Bon mon problème est peut être un peu compliqué expliqué comme ça alors je vais poser ma question autrement en gardant que le fond du problème:

Si je joins ma table "distrib" avec ma table "action" de telle sorte que repart.id_action=action.id j'aurais donc

une table du genre:
table: "action JOIN distrib"
------------------------------
| id_user |  label  | auteur |
------------------------------
|    1    | action1 |   4    |
|    2    | action1 |   4    |
|    3    | action1 |   4    |
|    2    | action2 |   1    |
|    4    | action2 |   1    |
------------------------------

Là dessus je veux extraire les infos concernant le user d'id=4, qu'il soit dans auteur ou id_user (ceci étant

précisé par le flag "role"). Le résultat devrait ressembler à ça, les résultats étant groupés par action:
-------------------------------------
| id_user |  label  | auteur | role |
-------------------------------------
|    1    |         |        |      |
|    2    | action1 |   4    |   1  |
|    3    |         |        |      |
-------------------------------------
|    2    | action2 |   1    |   0  |
|    4    |         |        |      |
-------------------------------------

J'essayé la requête:
SELECT a.label,
       IF(a.auteur=4 , 1 , 0 ) role,

FROM action a JOIN distrib d ON d.id_action = a.id

WHERE a.auteur=4 OR d.id_user=4

GROUP BY d.id_action

Ca me donne ça:
-------------------------------------
| id_user |  label  | auteur | role |
-------------------------------------
|    1    |         |        |      |
|    2    | action1 |   4    |   1  |
|    3    |         |        |      |
-------------------------------------
|    4    | action2 |   1    |   0  |
-------------------------------------

Donc comment récupérer les autres lignes lorsque l'utilisateur n'est pas auteur ?

J'avais pensé à ça:
SELECT a.label,
       IF(a.auteur=4 , 1 , 0 ) role,

FROM action a JOIN distrib d1 ON d1.id_action = a.id
              JOIN distrib d2 ON d2.id_action = a.id

WHERE a.auteur=4 OR d.id_user=4

GROUP BY d1.id_action

Mais ça me donne grosso modo quelque chose comme ça (avec des permutations étranges sur id_user):
-------------------------------------
| id_user |  label  | auteur | flag |
-------------------------------------
|    1    |         |        |      |
|    2    | action1 |   4    |   1  |
|    3    |         |        |      |
-------------------------------------
|    1    |         |        |      |
|    2    | action1 |   4    |   1  |
|    3    |         |        |      |
-------------------------------------
|    3    |         |        |      |
|    1    | action1 |   4    |   1  |
|    2    |         |        |      |
-------------------------------------
|    2    | action2 |   1    |   0  |
|    4    |         |        |      |
-------------------------------------

Il faudrait donc une sorte de mix entre les deux. Mais j'ai plus d'idées là...
0
Il y a sûrement quelque chose que je ne comprends pas dans ta demande.
Tu dis, si je comprends bien, que
-------------------------------------
| id_user |  label  | auteur | role |
-------------------------------------
|    1    |         |        |      |
|    2    | action1 |   4    |   1  |
|    3    |         |        |      |
-------------------------------------
|    4    | action2 |   1    |   0  |
-------------------------------------
est incomplet, qu(il manque la ligne
|    2    | action2 |   1    |   0  |

Mais en quoi cette ligne concerne-t-elle l'id_user 4 ?
quelle(s) étape(s) ai-je manquée(s) ?
0
wingover Messages postés 21 Date d'inscription lundi 15 décembre 2008 Statut Membre Dernière intervention 17 juin 2011
9 janv. 2009 à 20:29
Exactement.
Dans tous les cas, que l'individu soit auteur ou destinataire de l'action (c'est à dire que son id soit dans la colonne auteur ou id_user de la table action JOIN distrib) j'aimerais toujours récupérer la totalité des destinataires de l'action. Donc c'est bien un résultat de ce type que je cherche à avoir :
-------------------------------------
| id_user |  label  | auteur | role |
-------------------------------------
|    1    |         |        |      |
|    2    | action1 |   4    |   1  |
|    3    |         |        |      |
-------------------------------------
|    2    | action2 |   1    |   0  |
|    4    |         |        |      |
-------------------------------------
Or quand j'applique mon filtre d.id_user=4 dans la clause WHERE, la fameuse ligne disparait.

Dans la seconde requête j'avais imaginé récupérer cette ligne en joignant une deuxième fois la table distrib et qui ne subirait pas le filtre précédent. Je récupère effectivement la ligne manquante mais j'ai alors plein de lignes en trop dans le cas où l'utilisateur est acteur.

N'oublions pas que le cas "l'utilisateur est à la fois acteur et récepteur" peut exister.
0
wingover Messages postés 21 Date d'inscription lundi 15 décembre 2008 Statut Membre Dernière intervention 17 juin 2011
9 janv. 2009 à 20:34
En fait pour résumer, le vrai énoncé serait:

Je souhaite récupérer toutes les infos (acteur,destinataires) concernant les actions dans lequelles un utilisateur donné est impliqué que ce soit en tant qu'auteur ou en tant que destinataire.
0
Ceci est contradictoire avec ton premier exemple (post initial) dans lequel il n'apparaissait pas que l'action 2 avait eu 2 destinataires. Tu confirmes bien ta définition du post 4 ?
Si un utilisateur est à la fois auteur et destinataire, la ligne correspondante apparaît 2 fois : une fois avec role à 0 et une fois avec role à 1 ? Sinon, quelle valeur donner à role ?
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
wingover Messages postés 21 Date d'inscription lundi 15 décembre 2008 Statut Membre Dernière intervention 17 juin 2011
10 janv. 2009 à 14:01
Pour ce qui est de l'action2, il me semble bien que les deux destinataires apparaissent dans la table distrib de mon premier post.

Pour ce qui est du cas où l'utilisateur est à la fois auteur et destinataire, c'est vrai que ce cas est indéterminé au niveau de la sortie role. On va dire alors que dans ce cas role vaudra 2 ce qui devrait se traduire dans la requête par :
IF(a.auteur=4 ,IF(d.id_user=4 , 2 , 1 ) , 0 ) role,

Je confirme bien l'énoncé du post 4.

Je vais d'ailleurs compléter les tables action et distrib pour faire apparaître le dernier cas ainsi qu'un cas où l'utilisateur 4 n'est ni auteur ni destinataire.
Les tables à utiliser sont donc:
table: action
-------------------------
| id |  label  | auteur |
-------------------------
|  1 | action1 |   4    |
|  2 | action2 |   1    |
|  3 | action3 |   4    |
|  4 | action4 |   3    |
-------------------------

table : distrib
-----------------------
| id_action | id_user |
-----------------------
|     1     |    1    |
|     1     |    2    |
|     1     |    3    |
|     2     |    2    |
|     2     |    4    |
|     3     |    1    |
|     3     |    3    |
|     3     |    4    |
|     4     |    1    |
|     4     |    2    |
-----------------------

Le résultat pour l'utilisateur 4 devra donc être:
-------------------------------------
| id_user |  label  | auteur | role |
-------------------------------------
|    1    |         |        |      |
|    2    | action1 |   4    |   1  |
|    3    |         |        |      |
-------------------------------------
|    2    | action2 |   1    |   0  |
|    4    |         |        |      |
-------------------------------------
|    1    |         |        |      |
|    3    | action3 |   4    |   2  |
|    4    |         |        |      |
-------------------------------------
0
wingover Messages postés 21 Date d'inscription lundi 15 décembre 2008 Statut Membre Dernière intervention 17 juin 2011
10 janv. 2009 à 14:25
Ok, j'ai compris à quel niveau c'était contradictoire avec le premier post. Tu parlais surement de ce résultat où n'apparaissaient pas les destinataires de l'action 2.
-------------------------------------------------------
|  label  |    role   |         commentaires          |
-------------------------------------------------------
| action1 |   auteur  | destinataires:  pim,pam,poum  |
| action2 | recepteur | auteur: pim                   |
-------------------------------------------------------

C'est vrai que je n'affiche pas les autres destinataires lorsque l'utilisateur est recepteur, mais j'en aurai quand même besoin pour d'autres opérations que je ferai en php. Bref pour que l'exemple du premier post soit cohérent avec le reste le résultat souhaité serait plutôt :
------------------------------------------------
|  label  |    role   | auteur | destinataires |
------------------------------------------------
| action1 |   auteur  |  toto  | pim,pam,poum  |
| action2 | recepteur |   pim  |   pam,toto    |
------------------------------------------------
0
Je me suis pas expert en mysql, et mes quelques tentatives sont infructueuses.
Tu tiens à l'avoir en une seule requête pour l'amour de l'art, ou il y a un raison plus profonde ?
0
wingover Messages postés 21 Date d'inscription lundi 15 décembre 2008 Statut Membre Dernière intervention 17 juin 2011
10 janv. 2009 à 21:42
Pas spécialement mais je cherche à optimiser le truc. Mais c'est vrai qu'en faisant deux requêtes, une première pour récupérer la liste des actions et une seconde pour récupérer les destinataires associés à actions, ça ne devrait pas être trop lourd.
Sauf si quelqu'un a une meilleure idée.
0
Je ne suis pas sûr que faire une seule requête complètement tordue soit optimal par rapport à deux requêtes plus 'ordinaires'.
Mais tu as raison, peut-être qu'un gourou de passage pourra te donner satisfaction.
0
wingover Messages postés 21 Date d'inscription lundi 15 décembre 2008 Statut Membre Dernière intervention 17 juin 2011
12 janv. 2009 à 15:21
Et voilà, à force de chercher j'ai réussi avec cette requête:
SELECT GROUP_CONCAT(dd.id_user), a.label, a.auteur, 
           max( IF( a.auteur =4, IF( d.id_user =4, 2, 1 ) , 0 ) ) role
FROM distrib d
             JOIN action a ON ( a.id = d.id_action ) 
             JOIN distrib dd ON ( dd.id_action = d.id_action ) 
WHERE (
  d.id_user =4
  AND a.auteur <>4
)
OR (
  a.auteur =4
  AND d.id_user = dd.id_user
)
GROUP BY a.id

Le résultat est bien celui attendu dans le poste 6 en utilisant les tables de ce même post.

Pour obtenir le résultat du post 10 il n'y a qu'à joindre deux fois la table user du post 1, une fois sur a.auteur et une fois sur dd.id_user.

Mais comme dit toto, si un gourou du MySQL passe par là et vois mieux je reste intéressé.
0
Bravo ! Mais il faudrait que j'aie la tête beaucoup plus reposée que ça pour comprendre et apprécier !
0