IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage SQL Discussion :

left join avec critère sur la table de droite


Sujet :

Langage SQL

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2009
    Messages
    95
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Décembre 2009
    Messages : 95
    Par défaut left join avec critère sur la table de droite
    Bonjour à tous,

    J'ai ce soucis (imaginons qu'il s'agisse d'examens),
    trois tables (
    examens avec primary key = idExamen,
    eleves avec primary key= idEleve et
    examenPassé avec les foreignkey idExamen et idEleve).

    J'ai une table avec des personnes qui ont passé un (plusieurs) examens. Donc j'ai un identifiant pour la personne et un identifiant pour l'examen. Cet examen est constitué d'informations qui ne sont pas forcément toujours présente (par exemple la date d'un PV). Dans la table des examens toutes les informations sont identifiées par l'identifiant de l'examen ET un mot clé dans une seule et même colonne.
    Par exemple
    idLigne, idExamen, motClé, valeur
    1,12, dateExamen, 01/01/2019
    2,12, local, A151
    3,12, datePV, 05/01/2019
    4,15, dateExamen, 01/02/2019
    5,15, local, B12
    ==>pas encore de date de PV

    Donc on a un élève qui a passé l'examen 12 le 01/01/2019 dans le local A151 et à reçu son PV le 05/01/2019. Le même élève à passé l'examen 15 mais n'a pas encore reçu son PV.

    Bon, je dois récupérer toutes les informations de chaque examen sur UNE ligne. Hors pour l'examen 15, il n'y a pas encore de date de PV. On n'a donc pas une valeur à NULL, on n'a juste pas de ligne.

    J'ai essayé un truc de ce genre mais évidemment ça ne fonctionne pas:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    SELECT examenPasse.idEleve, t1.valeur, t2.valeur, t3.valeur
    FROM examenPasse 
    JOIN examen t1 ON examenPasse.idExamen=t1.idExamen
    JOIN examen t2 ON examenPasse.idExamen=t2.idExamen
    JOIN examen t3 ON examenPasse.idExamen=t3.idExamen
    WHERE 
    t1.motClé='dateExamen'
    AND t2.motClé='local'
    AND t3.motClé='datePV';
    Bien entendu j'ai testé le left join mais le problème ne vient pas de là, je n'ai juste pas de ligne avec le mot clé 'datePV' dans la table 'Examen'.

    Au final, je veux obtenir une ligne pour chaque examen du genre

    2, 01/01/2019, A151, 05/01/2019
    2, 01/02/2019, B12, NULL

    Merci de votre aide.

  2. #2
    Membre éprouvé
    Profil pro
    Inscrit en
    Juin 2010
    Messages
    73
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2010
    Messages : 73
    Par défaut
    Bonjour.

    La forme de la table est imposée ? Parce que je ne vois pas trop pourquoi faire plusieurs lignes pour un même examen...surtout si derrière tu attends les mêmes éléments à chaque fois.
    Dans un cas comme ça, je verrais plutôt :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    idExamen, local, dateExamen, datePV
    12, A151, 01/01/2019, 05/01/2019
    15, B12, 01/02/2019, [NULL]
    Tu constateras qu'avec cette construction ta requête devient soudainement plus simple
    En fait, souvent quand on est face à une requête "infaisable" c'est qu'il y a un problème dans la construction...


    Sinon, si tu veux/dois garder ton système :
    1. Récupère les (1..N) lignes de l'examen.
    2. Pour chaque MotClé attendu, s'il n'y a rien mettre la valeur [NULL].
    Pour le point 2, c'est plutôt à faire dans le programme.

  3. #3
    Modérateur
    Avatar de Waldar
    Homme Profil pro
    Sr. Specialist Solutions Architect @Databricks
    Inscrit en
    Septembre 2008
    Messages
    8 454
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Sr. Specialist Solutions Architect @Databricks
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2008
    Messages : 8 454
    Par défaut
    Vous avez deux solutions.
    La première, avec des LEFT JOIN comme vous l'avez pressenti.
    Pour ne pas transformer votre LEFT JOIN en INNER JOIN de manière implicite, on filtre dans la jointure :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        select ep.idEleve
             , ep.idExamen
             , t1.valeur   as dateExamen
             , t2.valeur   as local
             , t3.valeur   as datePV
          from examenPasse as ep
     left join examen      as t1 on t1.idExamen = ep.idExamen and t1."motClé" = 'dateExamen'
     left join examen      as t2 on t2.idExamen = ep.idExamen and t2."motClé" = 'local'
     left join examen      as t3 on t3.idExamen = ep.idExamen and t3."motClé" = 'datePV'
    ;
    On peut également réaliser un PIVOT, de cette façon :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
      select ep.idEleve
           , ep.idExamen
           , max(case ex."motClé" when 'dateExamen' then ex.valeur end) as dateExamen
           , max(case ex."motClé" when 'local'      then ex.valeur end) as local
           , max(case ex."motClé" when 'datePV'     then ex.valeur end) as datePV
        from examenPasse as ep
        join examen      as ex on ex.idExamen = ep.idExamen
       where ex."motClé" in ('dateExamen', 'local', 'datePV') -- Je suppose que dateExamen existe a minima, sinon transformez en left join comme ci-dessus.
    group by ep.idEleve
           , ep.idExamen;
    En fonction de l'indexation et du périmètre, l'une ou l'autre sera plus rapide.
    La première pour chercher sur quelques élèves et examens, la seconde si vous regarder en volume.

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2009
    Messages
    95
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Décembre 2009
    Messages : 95
    Par défaut
    Citation Envoyé par Waldar Voir le message
    Vous avez deux solutions.
    La première, avec des LEFT JOIN comme vous l'avez pressenti.
    Pour ne pas transformer votre LEFT JOIN en INNER JOIN de manière implicite, on filtre dans la jointure :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        select ep.idEleve
             , ep.idExamen
             , t1.valeur   as dateExamen
             , t2.valeur   as local
             , t3.valeur   as datePV
          from examenPasse as ep
     left join examen      as t1 on t1.idExamen = ep.idExamen and t1."motClé" = 'dateExamen'
     left join examen      as t2 on t2.idExamen = ep.idExamen and t2."motClé" = 'local'
     left join examen      as t3 on t3.idExamen = ep.idExamen and t3."motClé" = 'datePV'
    ;
    On peut également réaliser un PIVOT, de cette façon :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
      select ep.idEleve
           , ep.idExamen
           , max(case ex."motClé" when 'dateExamen' then ex.valeur end) as dateExamen
           , max(case ex."motClé" when 'local'      then ex.valeur end) as local
           , max(case ex."motClé" when 'datePV'     then ex.valeur end) as datePV
        from examenPasse as ep
        join examen      as ex on ex.idExamen = ep.idExamen
       where ex."motClé" in ('dateExamen', 'local', 'datePV') -- Je suppose que dateExamen existe a minima, sinon transformez en left join comme ci-dessus.
    group by ep.idEleve
           , ep.idExamen;
    En fonction de l'indexation et du périmètre, l'une ou l'autre sera plus rapide.
    La première pour chercher sur quelques élèves et examens, la seconde si vous regarder en volume.
    Bonjour et merci pour l'aide.

    La première requête à l'air de fonctionner, je teste plus en détail. ==> testé, c'est OK, je marque le post en résolu. Un tout tout grand merci.

    Pour la seconde, je dois dire que je ne la comprends pas du tout.

  5. #5
    Modérateur

    Profil pro
    dba
    Inscrit en
    Janvier 2010
    Messages
    5 643
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : dba

    Informations forums :
    Inscription : Janvier 2010
    Messages : 5 643
    Par défaut
    Bonjour,

    Pour comprendre la dernière requête de Waldar, vous pouvez enlever les MAX et le GROUP BY et ainsi voir le résultat "décomposé".
    Il faut aussi savoir que les fonctions d’agrégation comme MAX "éliminent" les NULLs

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2009
    Messages
    95
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Décembre 2009
    Messages : 95
    Par défaut
    Citation Envoyé par macgawel Voir le message
    Bonjour.

    La forme de la table est imposée ? Parce que je ne vois pas trop pourquoi faire plusieurs lignes pour un même examen...surtout si derrière tu attends les mêmes éléments à chaque fois.
    Dans un cas comme ça, je verrais plutôt :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    idExamen, local, dateExamen, datePV
    12, A151, 01/01/2019, 05/01/2019
    15, B12, 01/02/2019, [NULL]
    Tu constateras qu'avec cette construction ta requête devient soudainement plus simple
    En fait, souvent quand on est face à une requête "infaisable" c'est qu'il y a un problème dans la construction...


    Sinon, si tu veux/dois garder ton système :
    1. Récupère les (1..N) lignes de l'examen.
    2. Pour chaque MotClé attendu, s'il n'y a rien mettre la valeur [NULL].
    Pour le point 2, c'est plutôt à faire dans le programme.
    Bonjour,

    Merci pour cette réaction.

    Je ne suis pas administrateur de cette BD, je l'utilise comme elle a été développée, donc je n'ai pas le choix.

    Pour le point 2, c'est effectivement une solution, mais j'aurais préféré m'en sortir directement avec du SQL.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 3
    Dernier message: 16/10/2017, 22h11
  2. [AC-2010] Dcount avec critère sur 2 tables
    Par miliev83 dans le forum Access
    Réponses: 1
    Dernier message: 06/04/2016, 15h54
  3. Filtre avec critère sur autre table
    Par dvdavid2009 dans le forum Débuter
    Réponses: 4
    Dernier message: 18/08/2009, 13h48
  4. Requete SUM sur une table avec critère sur une autre
    Par wail00 dans le forum Langage SQL
    Réponses: 3
    Dernier message: 21/05/2007, 16h58
  5. Left join avec 3 tables
    Par MathiasMathias dans le forum Langage SQL
    Réponses: 1
    Dernier message: 10/04/2007, 00h45

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo