2008-10-09 5 views
16

Pourquoi l'ordre des tables est-il important lors de la combinaison d'une jointure interne &? ce qui suit échoue avec postgres:Jointure interne et jointure externe; l'ordre des tables est-il important?

SELECT grp.number AS number,  
     tags.value AS tag 
FROM groups grp, 
    insrel archiverel 
LEFT OUTER JOIN ownrel ownrel ON grp.number = ownrel.dnumber 
LEFT OUTER JOIN tags tags ON tags.number = ownrel.snumber 
WHERE archiverel.snumber = 11128188 AND  
     archiverel.dnumber = grp.number 

avec le résultat:

ERROR: invalid reference to FROM-clause entry for table "grp" LINE 5: LEFT OUTER JOIN ownrel ownrel ON grp.number = ownrel.d... 
^ HINT: There is an entry for table "grp", but it cannot be referenced from this part of the query. 

lorsque les groupes sont inversés dans les de tout travaux:

SELECT grp.number AS number,  
     tags.value AS tag 
FROM insrel archiverel, 
     groups grp 
LEFT OUTER JOIN ownrel ownrel ON grp.number = ownrel.dnumber 
LEFT OUTER JOIN tags tags ON tags.number = ownrel.snumber 
WHERE archiverel.snumber = 11128188 AND  
     archiverel.dnumber = grp.number 
+0

qui est très intéressant. Cela doit avoir quelque chose à voir avec le mélange des conventions de jointure. Je suis un peu surpris que la deuxième version fonctionne même! –

Répondre

20

Je crois que vous pouvez penser à ceci comme un problème de priorité d'opérateur.

Lorsque vous écrivez ceci:

FROM groups grp, 
    insrel archiverel 
LEFT OUTER JOIN ownrel ownrel ON grp.number = ownrel.dnumber 
LEFT OUTER JOIN tags tags ON tags.number = ownrel.snumber 

Je pense qu'il est interprété par l'analyseur comme ceci:

FROM groups grp, 
(
    (
    insrel archiverel 
    LEFT OUTER JOIN ownrel ownrel ON grp.number = ownrel.dnumber 
) 
LEFT OUTER JOIN tags tags ON tags.number = ownrel.snumber 
) 

Si oui, alors au plus profond join "grp" est non liée. Lorsque vous inversez les lignes avec "groups" et "insrel", la jointure la plus interne s'applique à "groups" et "ownrel", donc cela fonctionne.

Probablement cela fonctionnerait aussi bien:

FROM groups grp 
     JOIN insrel archiverel ON archiverel.dnumber = grp.number 
    LEFT OUTER JOIN ownrel ownrel ON grp.number = ownrel.dnumber 
    LEFT OUTER JOIN tags tags ON tags.number = ownrel.snumber 
WHERE archiverel.snumber = 11128188 
+0

Vous avez raison, en utilisant une jointure plutôt sur insel fonctionne en effet et semble plus logique ainsi –

5

Parce que dans le premier grp est ne fait pas partie de la jointure à laquelle appartient la clause ON.

+0

C'est le seul commentaire qui a du sens pour moi et il a corrigé ma requête. J'ai déplacé une table dans ma clause FROM pour être le dernier dans la clause FROM et il a corrigé l'erreur. –

4

Je ne sais pas ce qui cause ce comportement, si c'est un bug ou par conception, mais cela devrait fonctionner correctement si vous vous en tenez à une forme de jointure ou l'autre.

SELECT grp.number AS number,  
     tags.value AS tag 
FROM groups grp 
JOIN insrel archiverel ON archiverel.dnumber = grp.number 
LEFT OUTER JOIN ownrel ownrel ON grp.number = ownrel.dnumber 
LEFT OUTER JOIN tags tags ON tags.number = ownrel.snumber 
WHERE archiverel.snumber = 11128188 

Je serais intéressé d'en savoir plus si le comportement est voulu.

2

Pour une jointure interne, l'ordre des tables n'est pas important.

Pour une jointure externe, il s'agit de. Tous les lignes de la table sur le côté spécifié (est-ce une jointure GAUCHE ou DROITE) seront inclus, tandis que seules les lignes qui correspondent aux critères de jointure seront incluses dans la table de l'autre côté. Comme OUTER JOINS garde toutes les lignes d'un côté, on dit qu'elles augmentent (en général) les ensembles de résultats. INNER JOINS ne conserve que les lignes des deux côtés si elles correspondent, de sorte qu'elles sont dites (en général) pour réduire les ensembles de résultats. Ainsi, vous voulez typiquement faire vos JOINTES INTÉRIEURES avant les JOINTS EXTÉRIEURS (si possible).

Dans votre cas, c'est certainement dû à la mauvaise syntaxe A, B.

+0

true mais ce n'est pas l'ordre des jointures qui provoque le problème mais l'ordre dans la clause from –

+0

@Thies: WTFalse. L'ordre des jointures est spécifié dans la clause from, de sorte que "l'ordre des jointures" est vraiment "l'ordre des jointures dans la clause from", et que * est * le problème, car l'ordre est important. Si vous faites des jointures externes avant les jointures internes, les jointures internes réduisent le jeu de résultats que vous avez essayé de maintenir avec la jointure externe; par conséquent, vous devez faire les jointures (externes) inclusives après les jointures internes (exclusives); sinon les jointures externes sont inutiles. – Triynko

18

Je ne pense pas que quelqu'un ait vraiment compris cela ou l'ait très bien expliqué. Vous combinez des jointures «ancien style» (thêta) et «nouveau style» (ANSI), que je soupçonne fortement d'être regroupées de façons inattendues. Regardez cette façon:

SELECT * FROM a, b JOIN c ON a.x = c.x 

est comme dire

SELECT * FROM a, (b JOIN c on a.x = c.x) 

où la chose entre crochets représente un groupe de tables fusionnées en une seule table virtuelle, à joindre avec une theta-join contre 'une'. Évidemment, la table 'a' ne peut pas faire partie de la jointure, car elle ne sera jointe que plus tard. Inversez-le, et vous faites

SELECT * FROM b, (a JOIN c on a.x = c.x) 

ce qui est parfaitement compréhensible et si bien. Je ne suis pas sûr pourquoi vous n'utilisez pas la syntaxe de jointure ANSI pour tout cela, semble un peu étrange (et cruel à la personne qui doit le maintenir!)

+0

Ça doit être ce qui se passe. Je soupçonne que l'implémentation ne s'attendait pas à ce que les gens mélangent la syntaxe, et même s'ils l'ont fait, il n'y a pas de standard qui définit le comportement de la syntaxe mixte. –

+0

oui les mélanger est une vieille habitude qui encombre en effet la logique. –

Questions connexes