Partage
  • Partager sur Facebook
  • Partager sur Twitter

[SQL] jointure entre 3 tables

probleme de logique

6 mars 2014 à 14:17:36

Bonjour,

voilà mon probleme :

Les bases : je dispose d'une liste de jeux et de membres. Chaque membre dispose d'un id et d'un pseudo et peut posséder un jeu, identifié par un id et un nom. Les membres peuvent s'acheter des jeux, et s'inscrivent donc en tant qu'acheteur.

Supposons que je dispose de 3 tables simples dans ma base de données (MySQL)

Table membre :

id_membre     pseudo
    1           A
    2           B
    3           C
    4           D

Table jeu :

id_jeu       nom_jeu        possesseur(=id_membre)
    1         Mario             1
    2         Zelda             2
 

Table acheteurs :

id_jeu       acheteur(=id_membre)
   1              2
   1              3

Voici ce que j'aimerais afficher :

Le jeu (nom_jeu) dont l'id est (id_jeu),

est possédé par (pseudo),

Les acheteurs de ce jeu sont : (pseudo), (pseudo).

Ce qui me pose problème ici, c'est la liste des acheteurs. J'ai fait une première requête avec jointure permettant de compléter les 2premières lignes (en faisant une boucle). J'ai bien pensé faire une seconde requete dans la boucle pour aller chercher la liste d'acheteur selon l'id du jeu courant, mais il semblerait que niveau optimisation cette façon de faire soit à proscrire.

Donc comment puis-je faire cette opération en une seule requête ? (je ne comprends pas bien le système des jointures permettant de développer une liste dans une liste)...

Note : je code en php, si possible en PDO.

D'avance merci pour votre aide !

  • Partager sur Facebook
  • Partager sur Twitter
6 mars 2014 à 14:55:26

Tu peux relier les 3 tables par 2 jointures, comme il suit : (joueurs <-> jeux) <-> acheteurs. Autrement dit, tu auras 2 comparaisons à faire dans ta condition de jointure. Sauf que dans ton cas, il y a conflit entre les 2 pseudos possibles. Je ne vois pas vraiment de solution autre que de passer par une sous-requête, qui sert alors de table-source à la requête principale.

Ici, ma sous-requête permet d'associer un id de jeu à un pseudo de possesseur. Je peux alors utiliser dans la requête principale les pseudos de la table de membres pour les acheteurs uniquement.

SELECT req.pseudo AS possesseur,
   jeu.nom_jeu AS jeu,
   membres.pseudo AS acheteur
FROM jeu
INNER JOIN acheteurs
   ON jeu.id_jeu = acheteurs.id_jeu
INNER JOIN membres
   ON membres.id_membre = acheteurs.acheteur
INNER JOIN (
   SELECT jeu.id_jeu AS jeu,
      membres.pseudo AS pseudo
   FROM membres
   INNER JOIN jeu
      ON membres.id_membre = jeu.possesseur
) AS req
   ON req.jeu = jeu.id_jeu;



  • Partager sur Facebook
  • Partager sur Twitter
6 mars 2014 à 19:52:25

Wow wow...

Merci pour ta réponse, je vais étudier ca mais je t'avoue que là c'est encore du chinois pour moi.

Je ne savais pas qu'on pouvait faire des sous-requetes auxquelles on peut en plus donner un allias.

J'aimerais pousser le bouchon encore peu plus loin, pour tenter d'exploiter au maximum les capacités de cette requete, serait-il possible, en plus du résultat souhaité, obtenir le nombre d'acheteur ?

Pour une requete basique, j'utilise la fonction rowCount. Mais là, la fonction ne va pas comprendre ce que je lui demande de calculer ?

Autre question, y'a-t-il une différence vraiment significative entre une requete comme celle ci et une requete dans une boucle? La différence (de rapidité je suppose), si elle existe, peut-elle etre négligeable ?

Je demande ca d'abord pour ma culture mais aussi parce que si ta requete m'apporte des pistes et pourra m'aider dans pas mal de chose, pour cette histoire de jointure entre 3 tables j'ai peur d'avoir du mal à la transposer...

  • Partager sur Facebook
  • Partager sur Twitter
6 mars 2014 à 20:13:49

Salut !

Je pense que ton sujet concerne plus le SQL que le PHP, du coup, je le déplace dans le forum approprié.

-
Edité par Ymox 23 août 2017 à 9:21:57

  • Partager sur Facebook
  • Partager sur Twitter
6 mars 2014 à 20:53:56

elly2 a écrit:

Je ne savais pas qu'on pouvait faire des sous-requetes auxquelles on peut en plus donner un allias.

Si la sous-requête sert de table source, l'alias est même obligatoire sous peine d'erreur de syntaxe. :p
Pour les sous-requêtes n'ayant qu'une colonne, il est possible de les utiliser comme listes (Par exemple : WHERE machin NOT IN (SELECT id FROM latable)), et pour les sous-requête n'ayant qu'une ligne et une colonne, il est possible de les utiliser comme une simple valeur.

elly2 a écrit:

J'aimerais pousser le bouchon encore peu plus loin, pour tenter d'exploiter au maximum les capacités de cette requete, serait-il possible, en plus du résultat souhaité, obtenir le nombre d'acheteur ?

Oui c'est possible, à l'aide d'un GROUP BY et d'un COUNT(). Sous MySQL tu peux même le faire d'une façon peu catholique : mettre le GROUP BY et le COUNT() dans la sous-requête, ce qui signifie qu'un des champs (en l'occurence le pseudo du possesseur) ne sera ni groupé par le GROUP BY ni affecté par une fonction d'aggrégat (le COUNT() en est une). Sous MySQL ça passe (avec le risque de certaines valeurs incohérente, mais dans le cas présent il ne devrait pas y en avoir) mais se souvenir que c'est une entorse aux standards SQL et que donc d'autres systèmes provoqueront une erreur fatale. L'autre solution : une sous-sous-requête servant de table jointe dans la sous-requête. 3e solution : une sous-requête à joindre dans la requête principale, probablement la solution la plus élégante.

SELECT req.pseudo AS possesseur,
   jeu.nom_jeu AS jeu,
   membres.pseudo AS acheteur,
   req2.offres AS nb_acheteurs
FROM jeu
INNER JOIN acheteurs
   ON jeu.id_jeu = acheteurs.id_jeu
INNER JOIN membres
   ON membres.id_membre = acheteurs.acheteur
INNER JOIN (
   SELECT jeu.id_jeu AS jeu,
      membres.pseudo AS pseudo
   FROM membres
   INNER JOIN jeu
      ON membres.id_membre = jeu.possesseur
) AS req
   ON req.jeu = jeu.id_jeu
INNER JOIN (
   SELECT id_jeu AS jeu,
      COUNT(acheteur) AS offres
   FROM acheteurs
   GROUP BY id_jeu
) AS req2
   ON jeu.id_jeu = req2.jeu;

Souviens-toi : on peut toujours obtenir de la base de données la réponse qu'on attend, le tout est de savoir poser la bonne question. ;)

elly2 a écrit:

Pour une requete basique, j'utilise la fonction rowCount. Mais là, la fonction ne va pas comprendre ce que je lui demande de calculer ?

Mauvaise habitude que d'utilise PDOStatement::rowCount() pour compter le nombre de lignes répondues par une requête de sélection. Cette méthode est conçu pour trouver le nombre de lignes affectées par des requêtes modifiantes telles que les requêtes d'insertion, de mise à jour et de suppression de lignes. Pour les autres types de requêtes, son comportement est indéfini et donc non-fiable. Préfère faire un COUNT().

elly2 a écrit:

Autre question, y'a-t-il une différence vraiment significative entre une requete comme celle ci et une requete dans une boucle? La différence (de rapidité je suppose), si elle existe, peut-elle etre négligeable ?

La différence est plutôt énorme, dans la mesure où l'étape lente est la communication entre le script PHP et le serveur MySQL. Moins tu fais de requêtes, plus rapide tu seras, même si tu es amené à faire des méga-requêtes de 50 lignes. Encore que, 50 lignes, ça fait se poser la question de la pertinence de la structure de la table... xD

elly2 a écrit:

Je demande ca d'abord pour ma culture mais aussi parce que si ta requete m'apporte des pistes et pourra m'aider dans pas mal de chose, pour cette histoire de jointure entre 3 tables j'ai peur d'avoir du mal à la transposer...

N'hésite pas à fouiller la documentation, même quand tu ne cherches pas quelque chose pour un but précis : tu peux en apprendre pas mal juste via la simple curiosité. ;)
  • Partager sur Facebook
  • Partager sur Twitter
6 mars 2014 à 21:46:13

Pour l'instant je trouve que la documentation SQL est encore un peu complexe pour moi, j'y fais un saut de temps en temps mais je n'arrive pas toujours à trouver les réponses. Comme pour ce cas de figure par exemple, j'ai lu tout ce que je pouvais sur les jointures et pourtant j'ai pas su trouver la solution... Bref.

Je comprends ce que tu me dis concernant la vitesse des requêtes. Je vais tenter de les regrouper un peu (petit à petit et au fur et à mesure de ma compréhension sinon je fais planter mon site à coup sur !)

Enfin, tu parles de la pertinence de la structure d'une table si la requête comporte beaucoup de lignes. Ici, dans le cas que je viens d'exposer, la requete que tu proposes fait déjà 24 lignes. Est-ce beaucoup ? Aurais-je pu organiser mes tables différemment ?

  • Partager sur Facebook
  • Partager sur Twitter
6 mars 2014 à 22:29:59

Je ne sais pas pour les autres types de bases de données mais celle de MySQL est effectivement difficile à appréhender au début. Il faut apprendre à la lire avant de pouvoir en tirer partie, même quand t'es habitué à lire des documentations de langage. Mais l'effort en vaut la chandelle. ;)

Pour l'histoire des lignes, j'ai dit 50 lignes mais c'était une image. Si t'as 36 tables et sous-requête à amener ensembles, c'est là que ça peut amener à réflexion. Il y a les vues, qui sont des tables virtuelle représentant le résultat d'une requête, et s'utilisant comme des tables. La création de vues permet de simplifier par la suite des requêtes à lancer. C'est un exemple.

-
Edité par Darth Killer 6 mars 2014 à 22:32:17

  • Partager sur Facebook
  • Partager sur Twitter
6 mars 2014 à 23:22:55

Merci pour toutes ces informations !

Je vais tester ce que tu m'as proposé. Si cela fonctionne comme je l'espère je marquerai le sujet comme résolu !

  • Partager sur Facebook
  • Partager sur Twitter
8 mai 2023 à 2:08:39 - Message modéré pour le motif suivant : Merci de créer votre propre sujet


8 mai 2023 à 10:31:09

@BrunoRixNdandula Bonjour, merci de ne pas squatter le sujet des autres pour une nouvelle question, créer votre propre sujet dans le respect des règles du forum à savoir qu'un message commence par des règles de politesses (Un bonjour ou des salutations à la communauté et se termine par des remerciements par avances pour les futures réponses), la description de votre problème et le code que vous avez écrit inséré sur le forum à l'aide de l'outil d'intégration de code soit le bouton code </>.

Déterrage

Citation des règles générales du forum :

Avant de poster un message, vérifiez la date du sujet dans lequel vous comptiez intervenir.

Si le dernier message sur le sujet date de plus de deux mois, mieux vaut ne pas répondre.
En effet, le déterrage d'un sujet nuit au bon fonctionnement du forum, et l'informatique pouvant grandement changer en quelques mois il n'est donc que rarement pertinent de déterrer un vieux sujet.

Au lieu de déterrer un sujet il est préférable :

  • soit de contacter directement le membre voulu par messagerie privée en cliquant sur son pseudonyme pour accéder à sa page profil, puis sur le lien "Ecrire un message"
  • soit de créer un nouveau sujet décrivant votre propre contexte
  • ne pas répondre à un déterrage et le signaler à la modération

Je ferme ce sujet. En cas de désaccord, me contacter par MP.

  • Partager sur Facebook
  • Partager sur Twitter