Partage
  • Partager sur Facebook
  • Partager sur Twitter

Récupération de données récentes sur 3 tables

    1 septembre 2019 à 19:13:35

    Bonjour,

    le contexte est le suivant : je développe un forum. Chaque forum dispose de sujets (threads) qui eux-mêmes contiennent des messages.

    Modèle relationnel

    USER (id, pseudo...)

    FORUM (id, category_id, title...)

    THREAD (id, author_id, forum_id, title, created_at)

    MESSAGE (id, author_id, thread_id, published_at, content, updated_at)

    Concepteur phpMyAdmin


    A noter

    • L'auteur d'un thread peut être NULL (son compte a été supprimé).
    • L'auteur d'un message peut être NULL (son compte a été supprimé).


    Je souhaiterais, en une requête SQL unique :

    1 - Récupérer l'ensemble des threads d'un forum donné

    2 - Ordonnés par date du dernier message posté dans celui-ci

    3 - Avec le nombre de messages de chacun de ses threads

    4 - Avec l'auteur (pseudo) de celui qui a crée le thread

    5 - Avec l'auteur (pseudo) du dernier message

    Cette requête sera par la suite utilisée avec Doctrine (ORM PHP).

    Pour 1, 2, 3 et 4 c'est ok, je bloque sur la 5.


    SELECT t.*, u.pseudo AS author_pseudo, COUNT(m.id) AS total_messages, MAX(m.published_at) AS last_message_at
    FROM thread t
    LEFT JOIN user u ON t.author_id = u.id
    INNER JOIN message m ON t.id = m.thread_id
    WHERE forum_id = 4
    GROUP BY t.id
    ORDER BY last_message_at DESC


    Cette requête, dans l'exemple du forum id 4, me retourne donc tout ce qu'il me faut, excepté le pseudo de l'auteur du dernier message.


    Ce que j'ai essayé

    - Ajouter directement m.author_id AS last_message_by dans le SELECT, évidemment erreur :

    SELECT list is not in GROUP BY clause and contains nonaggregated column 'sym_forum.m.author_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

    - Ajouter une jointure entre messages & users puis ajouter le champ dans le select, même erreur


    Un collègue m'a produit le code suivant (beaucoup trop complexe à mon niveau, je n'ai pas vu les notions utilisées) :

    SELECT t.*, u.pseudo AS author_pseudo, m_last.published_at AS last_message_at, u_last.pseudo AS last_author
                
    FROM thread t
                
    LEFT JOIN user u ON t.author_id = u.id
    JOIN message m_last on t.id = m_last.thread_id
    LEFT JOIN user u_last on u_last.id = m_last.author_id
                            
    WHERE forum_id = :forum
    AND CONCAT(m_last.published_at, m_last.id ) = (SELECT MAX(CONCAT(tmp.published_at, tmp.id )) FROM message tmp where t.id = tmp.thread_id)
                            
    ORDER BY last_message_at DESC

    Cette-fois ci j'obtiens bien le pseudo de l'utilisateur qui a posté le dernier message mais plus le nombre total de messages dans le thread en question.

    Vous l'aurez compris, dans ma solution "basique" il me manque un pseudo, dans sa solution "complexe" il manque le nombre total de messages.

    Je ne sais plus comment raisonner, je reste bloqué.


    Je vous remercie par avance de votre aide ! ;)

    -
    Edité par -Steven 1 septembre 2019 à 19:20:12

    • Partager sur Facebook
    • Partager sur Twitter

    Discord d'entraide symfony

    Soldat du code et enfant de la technologie.

      2 septembre 2019 à 13:43:21

      Bonjour,

      Je pense que le "plus simple" est de décomposé ton besoin en sous-requêtes voire en vues...

      L'idée c'est de créer, dans un premier temps, une vue qui te retourne les données du dernier message par thread :

      CREATE VIEW vue_dernier_message AS
      SELECT M.*, LM.nb_messages
      FROM
      	(
      		-- Date dernier message par thread
      		SELECT thread_id, MAX( published_at ) AS max_date, COUNT(*) AS nb_messages
      		FROM message
      		GROUP BY thread_id
      	) LM
      		INNER JOIN message M
      			ON LM.thread_id = M.id
      			AND LM.max_date = M.published_at;

      Cette vue calcule dans une sous-requête la date du dernier message par thread et le nombre de messages par thread, puis récupère le contenu du dernier message.

      Tu peux maintenant utiliser ta vue comme bon te semble pour faire ta requête globale :

      SELECT
      	t.*,
      	u.pseudo AS author_pseudo,
      	DM.published_at AS last_message_at,
      	u_last.pseudo AS last_author,
      	DM.nb_messages
      FROM
      	thread t
      		INNER JOIN vue_dernier_message DM
      			ON t.id = DM.thread_id
      		LEFT JOIN user u
      			ON t.author_id = u.id
      		LEFT JOIN user u_last
      			on u_last.id = dm.author_id
      WHERE forum_id = :forum
      ORDER BY last_message_at DESC;

      Seul problème, mais qui vaut pour toute solution ... deux messages postés en simultané pour le même thread ...

      • Partager sur Facebook
      • Partager sur Twitter
      Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL
        7 septembre 2019 à 11:33:49

        Bonjour,

        merci pour ta réponse.

        La vue crée possède donc cette structure.

        En exécutant la requête globale sur un forum existant j'obtiens exactement la structure que je souhaite :

        Cependant, le résultat est malheureusement vide.

        J'ai bien vérifié, le forum existe bel et bien et dispose de threads.

        Si vous avez une idée pour m'éclairer...

        Merci !

        • Partager sur Facebook
        • Partager sur Twitter

        Discord d'entraide symfony

        Soldat du code et enfant de la technologie.

          7 septembre 2019 à 23:17:21

          Poste le SQL de ta vue et de la requête exécutée si tu veux bien ...

          Merci.

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

            C'est exactement ce qui provient de ton dernier message.

            J'ai uniquement remplacé le :forum.

            • Partager sur Facebook
            • Partager sur Twitter

            Discord d'entraide symfony

            Soldat du code et enfant de la technologie.

              9 septembre 2019 à 9:36:40

              Alors il faut décomposer la requête pour comprendre ce qui ne va pas ...

              Si tu exécutes seulement ceci :

              SELECT thread_id, MAX( published_at ) AS max_date, COUNT(*) AS nb_messages
              FROM message
              GROUP BY thread_id

              Obtiens-tu des valeurs, et dans ces valeurs vois-tu les thread_id souhaités ?

              Si oui, si tu exécutes la vue complète :

              SELECT M.*, LM.nb_messages
              FROM
              	(
              		-- Date dernier message par thread
              		SELECT thread_id, MAX( published_at ) AS max_date, COUNT(*) AS nb_messages
              		FROM message
              		GROUP BY thread_id
              	) LM
              		INNER JOIN message M
              			ON LM.thread_id = M.id
              			AND LM.max_date = M.published_at;

              Obtiens-tu des valeurs, et dans ces valeurs vois-tu les thread_id souhaités ?

              Si oui ... c'est étrange ... que retourne cette requête de contrôle :

              SELECT t.*
              FROM
              	thread t
              		LEFT JOIN vue_dernier_message DM
              			ON t.id = DM.thread_id
              WHERE DM.thread_id IS NULL

              Cette requête te retourne la liste des threads qui n'ont pas de lien dans la vue. Obtiens-tu des valeurs ?

              • Partager sur Facebook
              • Partager sur Twitter
              Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL
                20 septembre 2019 à 18:55:17

                Si j’exécute seulement

                SELECT thread_id, MAX( published_at ) AS max_date, COUNT(*) AS nb_messages
                FROM message
                GROUP BY thread_id

                J'obtiens en effet le résultat pour chacun des threads.

                Si j'exécute la vue complète :

                SELECT M.*, LM.nb_messages
                FROM
                    (
                        -- Date dernier message par thread
                        SELECT thread_id, MAX( published_at ) AS max_date, COUNT(*) AS nb_messages
                        FROM message
                        GROUP BY thread_id
                    ) LM
                        INNER JOIN message M
                            ON LM.thread_id = M.id
                            AND LM.max_date = M.published_at;

                Je n'obtiens aucun résultat.

                Pour ce qui est de la requête de contrôle :

                Autrement dit, le même résultat qu'un simple SELECT * FROM thread.

                Merci

                • Partager sur Facebook
                • Partager sur Twitter

                Discord d'entraide symfony

                Soldat du code et enfant de la technologie.

                  20 septembre 2019 à 22:53:14

                  J'ai fait une erreur ... dans la vue la jointure doit se faire sur thread_id et pas sur id ...

                  SELECT M.*, LM.nb_messages
                  FROM
                      (
                          -- Date dernier message par thread
                          SELECT thread_id, MAX( published_at ) AS max_date, COUNT(*) AS nb_messages
                          FROM message
                          GROUP BY thread_id
                      ) LM
                          INNER JOIN message M
                              ON LM.thread_id = M.thread_id -- correction ici
                              AND LM.max_date = M.published_at;
                  • Partager sur Facebook
                  • Partager sur Twitter
                  Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL
                    21 septembre 2019 à 19:12:52

                    Oui ça fonctionne effectivement mais du coup mais je ne suis pas sûr de bien te suivre.

                    Cette requête me retourne le dernier message complet de chaque thread, ce qui n'a plus rien à voir avec la structure que je souhaitais recevoir.

                    Est-ce moi qui est loupé une étape ? Sinon je vais peut-être trouver une autre solution avec ce que tu as pu concocter.

                    Dans tous les cas, je te remercie !

                    • Partager sur Facebook
                    • Partager sur Twitter

                    Discord d'entraide symfony

                    Soldat du code et enfant de la technologie.

                      23 septembre 2019 à 10:42:45

                      -Steven a écrit:

                      ce qui n'a plus rien à voir avec la structure que je souhaitais recevoir

                      -Steven a écrit:

                      Je souhaiterais, en une requête SQL unique :

                      1 - Récupérer l'ensemble des threads d'un forum donné

                      2 - Ordonnés par date du dernier message posté dans celui-ci

                      3 - Avec le nombre de messages de chacun de ses threads

                      4 - Avec l'auteur (pseudo) de celui qui a crée le thread

                      5 - Avec l'auteur (pseudo) du dernier message

                      Maintenant que la vue est corrigée, tu peux rédiger ta requête principale comme bon te semble ... Si le résultat attendu est celui donné dans ton dernier message, alors :

                      SELECT
                      	t.id,
                      	t.author_id,
                      	t.forum_id,
                      	t.created_at,
                      	u.pseudo AS author_pseudo,
                      	dm.published_at AS last_message_at,
                      	u_last.pseudo AS last_author,
                      	dm.nb_messages
                      FROM
                      	thread t
                      		INNER JOIN vue_dernier_message dm
                      			ON t.id = dm.thread_id
                      		INNER JOIN user u
                      			ON t.author_id = u.id
                      		INNER JOIN user ul
                      			on ul.id = dm.author_id
                      WHERE t.forum_id = :forum
                      ORDER BY dm.last_message_at DESC;
                      • Partager sur Facebook
                      • Partager sur Twitter
                      Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL

                      Récupération de données récentes sur 3 tables

                      × 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