8606 sujets

Développement web côté serveur, CMS

Bonjour à tous
J'ai une table "Members" qui contient les membres de l'association.
Les membres peuvent appartenir à un ou plusieurs groupes.
Pour ce faire, j'ai une table "Groups" qui contient des paires "nom de groupe <-> identifieur de membre"
Je voudrais récupérer en une seule requête la liste des membres et la liste des groupes auxquels ils appartiennent.
J'ai essayé d'utiliser

SELECT mb.*,
    CONCAT_WS(';', 
         (SELECT g.groupName
           FROM Groups as g
           WHEN g.mbID = mb.mbID
        )
  ) AS groupList
FROM Members as mb;

Cette syntaxe ne fonctionne pas, mais je ne comprends pas pourquoi et ce qu'il faudrait faire pour récupérer le résultat recherché.
Une idée ?
Salut PapyJP,

Un simple LEFT JOIN ne suffirait pas à récupérer le résultat que tu veux ?

SELECT *
FROM Members
LEFT JOIN Groups ON Members.id = Groups.mbID
Merci de ta réponse Pour autant que je comprenne ça va me rendre autant de lignes que de groupes auxquelles le membre appartient. Si je veux la liste des quelques 25 membres actifs ça va me faire 100 à 150 lignes qu’il va falloir trier et consolider en php. C’est ce que je voudrais éviter
Modifié par PapyJP (04 Dec 2022 - 20:05)
Bonjour,

Avec group_concat.

SELECT mb.id, mb.nom, group_concat(groups.nom) as groupes
FROM Members as mb;
INNER JOIN Groups ON Members.id = Groups.mbID
group by mb.id,
Tu peux choisir le séparateur de la concaténation, faire un order by, ou demander des résulats distincts pour éviter les répétitions, quoi que dans ton cas, je ne pense pas qu'un membre soit plusieurs fois dans le même groupe ... par exemple:

SELECT GROUP_CONCAT(DISTINCT Groups.nom ORDER BY Groups.nom SEPARATOR ', ')...

Avec un left join, tu devrais aussi obtenir les membres qui ne sont dans aucun groupe.
Modifié par loicbcn (05 Dec 2022 - 09:16)
Meilleure solution
Bonjour,

je ne pense pas qu’en SQL, on puisse avoir une sortie d’un select avec un nombre de colonnes différents suivant les lignes.
Si tu as une base de données avec deux groupes (footballeur et français) du genre

Lionel Messi     footballeur
Tony Parker      français
Kylian Mbappé    footballeur         français

Tu ne pourras pas avoir le résultat d’une commande select avec la ligne Mbappé qui a 3 colonnes et les autres lignes seulement 2 colonnes.

Ce que je ferai est ceci :
select mb.nom, gr.nom from MembresGroupes as mg inner join Membres as mb on mb.id = mg.idMembre inner join Groupes as gr on gr.id = mg.idGroupe;
qui renvoie :
Lionel Messi | footballeur
Tony Parker | français
Kylian Mbappé | footballeur
Kylian Mbappé | français
Modifié par adrien881 (05 Dec 2022 - 11:47)
Merci de ta réponse
C'est justement ce que je ne tiens pas à faire : ici tu as deux lignes avec Kylian Mbappé.
Je cherche à savoir s'il existe une solution qui permettrait de n'envoyer qu'une ligne par personne.
Pour en rester à ton exemple je voudrais obtenir

Lionel Messi     footballeur
Tony Parker      français
Kylian Mbappé    footballeur;français

La fonction CONTACT_WS(";", a, b, c) rend une seule chaine de caractères a;b;c, j'espérais donc pouvoir l'utiliser, mais apparemment on ne peut pas l'appliquer au résultat d'une sous-requête qui renvoie une colonne.
Hello Papy,

Selon moi ton modèle de données est incorrect. Si un membre peut appartenir à plusieurs groupes et qu'un groupe peut contenir plusieurs membre, tu es dans une relation de type "many-to-many" (comme dans ton précédent sujet). Il te manque théoriquement une table intermédiaire et cela change complètement requête.

Je commencerai par là à ta place Smiley smile
Il n’y a pas de "table des groupes" car les groupes n’ont aucune autre information que leur nom de groupe.
Ma table est donc bien ce que tu appelles une "table intermédiaire" sauf que l’identifier de la deuxième table suffit. Je ne vais tout de même pas créer une table des groupes du genre

GoupId      GroupName
group1        group1
group2       group2
……….

pour faire joli.
Bien sûr si un jour j’ai besoin d’informations supplémentaires pour les groupes je ferai une table de plus.
@loicbcn
Merci de ta réponse
Ce matin j’avais sauté la lecture de ton message. Je vais essayer ce que tu proposes

après les tests
Merci, c'est tout à fait ce que je cherchais
Modifié par PapyJP (05 Dec 2022 - 16:22)
PapyJP a écrit :
Je ne vais tout de même pas créer une table des groupes du genre

GoupId      GroupName
group1        group1
group2       group2
……….

En fait, tu n'as pas le choix si tu souhaite respecter la théorie derrière les modèles de données SQL. Et c'est pas pour rien. Dans ton cas, la valeur GroupName va apparaître autant de fois qu'il y a de membres dans le groupe. Si tu souhaite modifier le nom du groupe c'est toute ces lignes que tu devras modifier à la fois. Si tu respecte la règle, c'est qu'une seule entrée à modifier. Et c'est juste un exemple parmi d'autres.
Modifié par Anymah (05 Dec 2022 - 17:17)
Dans mon modèle actuel si je voulais changer le nom d’un groupe — ce qui a peu de chances d’arriver car ces noms de groupes sont stables depuis au moins 12 ans — il suffirait d’une seule requête pour le faire.
Si je voulais avoir d’autres propriétés que le nom (par exemple afficher un nom différent de groupId) il suffirait de créer une nouvelle table dont l’index serait le groupID. Je n’aurais rien à modifier au code actuel, car si on crée une nouvelle propriété c’est évidemment du code nouveaux qui serait nécessaire pour l’exploiter.
Salut,

ce qui change avec ton modèle ce n'est pas le nombre de requête à écrire mais le nombre de ligne impacté par la modification. Dans les 2 cas en gros la requête ça serait ça :

UPDATE Groups set groupName = "NouveauNom" 
WHERE groupName = "VieuxNom" 

Dans ton cas il y a autant de ligne à changer qu'il y a de gens dans le groupe que tu modifies.

Si tu avais une table pivot en plus, la modification du nom du groupe n'affecte plus qu'une seule ligne dans la table de groupe et cela ne change rien à ta table pivot.

Et le jour ou tu veux faire évoluer ta table groupe, tu peux rajouter des informations sans modifier ton code.
Ta suggestion d'utiliser le vieux groupName comme groupID, c'est pas mal de la bidouille aussi Smiley sweatdrop . Plus propre de basculer sur un vrai ID et de modifier la table pivot pour qu'elle utilises ton ID.
Excuse moi de ne pas avoir répondu plus tôt, j'étais parti quelques jours de chez moi.

Si une propriété est une liste d'éléments on n'a le choix que d'utiliser un champ avec des séparateurs ou faire une table auxiliaire.
Pour donner un exemple différent de mes "groupes d'utilisateurs" qui prêtent à confusion, prenons le cas d'une couleur: un objet peut avoir une ou plusieurs couleurs, et la même couleur peut s'appliquer à différents objets.
Dans un objet "cravate" on peut avoir un champ "couleurs" qui contiendra "bleu;rouge;vert", ce qui signifie que que les motifs de la cravate contiennent ces couleurs.
Mais si pour une autre raison j'ai besoin de retrouver toutes les cravates qui ont du rouge dans leurs couleurs, je suis amené à créer une table intermédiaire permettant d'indiquer que telle couleur et telle cravate sont "associées".
Pour autant on n'a pas besoin de créer une table "couleurs", sauf bien entendu on a d'autres besoins, auquel cas on pourra éventuellement la créer plus tard.

Pour le reste je suis d'accord que le champ "groupName" devrait plutôt s'appeler "groupID" en prévision du jour ou je serai amené à créer une table des groupes. Je vais regarder si je peux facilement faire cette modification dans mes applications. Comme elles ne sont pas nombreuses, je dois pouvoir corriger cette erreur.
Modifié par PapyJP (09 Dec 2022 - 11:43)