Partage
  • Partager sur Facebook
  • Partager sur Twitter

Requete avec table de jonction et groupage

Sujet résolu
    18 octobre 2018 à 18:38:26

    Bonjour, voici tout d'abord mon schema de db:

    Rien de sorcier, il s'agit d'une table contenant des "fichiers", une table contenant des "tags" et une table de jonction (files_tags) pour associer des tags à des fichiers.

    Je cherche a faire un requête capable de me retourner en une passe (semble plus optimisé) tous les fichiers ainsi que leur tags:

    SELECT files.file_id, files.file_name, tags.tag_id FROM files, tags, files_tags WHERE files.file_id = files_tags.file_id AND files_tags._tag_id = tags.tag_id

    Le problème de cette première requête: je ne récupère que les fichiers avec au moins un tag associé et plusieurs rows avec les même file_id sont retournées.

    SELECT files.file_id, files.file_name, GROUP_CONCAT(tags.tag_id) FROM files, tags, files_tags WHERE files.file_id = files_tags.file_id AND files_tags._tag_id = tags.tag_id

    Cette fois les ids des tags sont bien groupés mais je n'ai toujours pas les fichiers sans tags...

    Pourriez vous m'aider ? 

    Merci infiniment d'avance :p


    • Partager sur Facebook
    • Partager sur Twitter
      18 octobre 2018 à 22:28:41

      Bonjour,

      C'est les limites des pseudo-jointures que tu fais. Utilise LEFT JOIN.

      • Partager sur Facebook
      • Partager sur Twitter
        19 octobre 2018 à 9:07:41

        Bonjour,

        philodick a écrit:

        Utilise LEFT JOIN

        Et oui ...

        SELECT
        	F.file_id,
        	F.file_name,
        	GROUP_CONCAT(T.tag_name) AS tags
        FROM
        	files F
        		LEFT JOIN files_tags FT
        			ON F.file_id = FT.file_id
        		LEFT JOIN tags T
        			ON FT._tag_id = T.tag_id
        GROUP BY
        	F.file_id,
        	F.file_name
        • Partager sur Facebook
        • Partager sur Twitter
        Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL
          19 octobre 2018 à 10:23:49

          C'est super merci. En fait ce qui me manquait le plus c’était le GROUP BY. Sans lui, avec le GROUP_CONCAT, le LEFT JOIN ne "fonctionnait pas" (je ne recevais que les fichiers avec un tag).

          Un grand merci

          • Partager sur Facebook
          • Partager sur Twitter
            19 octobre 2018 à 12:10:38

            En même temps une fonction de regroupement (ici GROUP_CONCAT) sans GROUP BY ... tu tends le bâton :D

            C'est d'ailleurs un travers "rigolo" de MySQL d'accepter cela, les autres SGBDR t'auraient jeté comme des malpropres :p

            • Partager sur Facebook
            • Partager sur Twitter
            Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL
              19 octobre 2018 à 15:37:52

              Heu je viens de rencontrer un autre soucis du coup: si ne souhaite que les fichiers qui ont un tag_id spécifique mais qui retourne quand même tous les tags associés à ce fichier, comment faire ?

              SELECT
                  F.file_id,
                  F.file_name,
                  GROUP_CONCAT(T.tag_name) AS tags
              FROM
                  files F
                      LEFT JOIN files_tags FT
                          ON F.file_id = FT.file_id
                      LEFT JOIN tags T
                          ON FT._tag_id = T.tag_id
              WHERE
                T.tag_id = 1
              GROUP BY
                  F.file_id,
                  F.file_name

              Cette requete ne va me retourner que [1] comme tags pour les fichiers

              -
              Edité par lifaon74 19 octobre 2018 à 15:50:32

              • Partager sur Facebook
              • Partager sur Twitter
                19 octobre 2018 à 15:56:07

                Une solution serait de rajouter une jointure vers les tags avec ta condition :

                SELECT
                	F.file_id,
                	F.file_name,
                	GROUP_CONCAT(T.tag_name) AS tags
                FROM
                	files F
                		INNER JOIN files_tags FT1
                			ON F.file_id = FT1.file_id
                		INNER JOIN files_tags FT
                			ON F.file_id = FT.file_id
                		INNER JOIN tags T
                			ON FT._tag_id = T.tag_id
                WHERE FT1.tag_id = 1
                GROUP BY
                	F.file_id,
                	F.file_name

                Ici plus besoin des jointures externes (LEFT JOIN) puisque ton fichier a forcément au moins un tag.

                • Partager sur Facebook
                • Partager sur Twitter
                Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL
                  19 octobre 2018 à 18:02:07

                  Hum, ça fonctionne mais ça a aussi ses limites:

                  - si j'omets le where, la concatenation double les tags (ça à la limite ok, je peux les virer à la fin) => [1, 1, 2, 2]

                  - si le where vaut FT1.tag_id = 1 AND FT1.tag_id = 2, alors il n'y a pas de résultats (pas ok >_<) => ça semble logique aussi car 1 != 2, mais du coup je ne vois pas comment avoir les fichiers qui ont à la fois le tag 1 et 2

                  - si le where vaut FT.tag_id = 1 AND FT1.tag_id = 2, alors il n'y a que [1] comme tag (pas ok non plus :'( )

                  Mon objectif final si cela peut clarifier les choses:

                  Obtenir le plus rapidement possible (donc en une passe je suppose) une liste de fichiers avec certaines contraintes (ex: has id, has tags, has name, etc...), ces contraintes doivent rester génériques et sont potentiellement complexes (ex: fichier avec nom 01.png et avec tag 1 mais pas tag 2). Les rows retournées doivent être uniques en fonction de l'id du fichier et pour chaque fichier ont doit avoir la liste des tags (ids) qui lui sont associés.

                  Quelle serait la meilleure démarche pour obtenir ce résultat ?

                  -
                  Edité par lifaon74 19 octobre 2018 à 18:03:27

                  • Partager sur Facebook
                  • Partager sur Twitter
                    19 octobre 2018 à 18:17:19

                    lifaon74 a écrit:

                    si j'omets le where, la concatenation double les tags

                    Si tu veux omettre le WHERE, c'est alors toute la jointure qu'il faut virer ...

                    lifaon74 a écrit:

                    si le where vaut FT1.tag_id = 1 AND FT1.tag_id = 2, alors il n'y a pas de résultats

                    C'est normal, pour un enregistrement donné, le tag_id ne peut pas être égal à 1 et à 2 en même temps ...

                    lifaon74 a écrit:

                    si le where vaut FT.tag_id = 1 AND FT1.tag_id = 2, alors il n'y a que [1] comme tag

                    Normal aussi puisque tu limites la jointure vers les tags ... Si tu veux tous les tags il ne faut pas mettre de condition sur FT.

                    lifaon74 a écrit:

                    Obtenir le plus rapidement possible (donc en une passe je suppose) une liste de fichiers avec certaines contraintes (ex: has id, has tags, has name, etc...), ces contraintes doivent rester génériques et sont potentiellement complexes

                    A contraintes complexes, réponses complexes :p

                    Ce que tu cherches à faire est une intersection d'ensembles, SQL prévoit ceci avec le mot clé INTERSECT (MySQL ne l'accepte pas au passage).

                    Il faut alors rédiger ta requête comme ceci :

                    SELECT file_id
                    FROM ...
                    WHERE ...
                    
                    INTERSECT
                    
                    SELECT file_id
                    FROM ...
                    WHERE ...
                    
                    INTERSECT
                    
                    etc.

                    Sinon tu peux approfondir les jointures. Avec une jointure par condition imposée :

                    -- Fichier (et tous ses tags) ayant les tags 1 ET 2
                    SELECT
                        F.file_id,
                        F.file_name,
                        GROUP_CONCAT(T.tag_name) AS tags
                    FROM
                        files F
                            INNER JOIN files_tags FT1
                                ON F.file_id = FT1.file_id
                                AND FT1.tag_id = 1
                            INNER JOIN files_tags FT2
                                ON F.file_id = FT2.file_id
                                AND FT2.tag_id = 2
                            INNER JOIN files_tags FT
                                ON F.file_id = FT.file_id
                            INNER JOIN tags T
                                ON FT._tag_id = T.tag_id
                    GROUP BY
                        F.file_id,
                        F.file_name

                    Je conçois que ce soit relou de construire ta requête ainsi, mais ce sera le plus performant ...

                    Attention aussi à la différence entre ET et OU ... ici c'est du ET ...

                    • Partager sur Facebook
                    • Partager sur Twitter
                    Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL

                    Requete avec table de jonction et groupage

                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                    × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                    • Editeur
                    • Markdown