Comment une jointure externe gauche peut-elle renvoyer plus d'enregistrements qu'il n'en existe dans la table de gauche?

165

J'ai une jointure externe gauche très basique pour renvoyer tous les résultats de la table de gauche et quelques informations supplémentaires d'une table beaucoup plus grande. La table de gauche contient 4935 enregistrements. Pourtant, lorsque JE LEFT OUTER JOIN il à une table supplémentaire, le nombre d'enregistrements est considérablement plus grand.

Autant que je sache, c'est un évangile absolu qu'une JOINTURE EXTÉRIEURE GAUCHE renverra tous les enregistrements de la table de gauche avec les enregistrements correspondants de la table de droite et des valeurs nulles pour toutes les lignes qui ne peuvent pas être mises en correspondance, en tant que tel, je crois comprendre qu'il devrait impossible de renvoyer plus de lignes qu'il n'en existe dans le tableau de gauche, mais ça se passe quand même!

La requête SQL suit:

SELECT     SUSP.Susp_Visits.SuspReason, SUSP.Susp_Visits.SiteID
FROM         SUSP.Susp_Visits LEFT OUTER JOIN
                      DATA.Dim_Member ON SUSP.Susp_Visits.MemID = DATA.Dim_Member.MembershipNum

Peut-être ai-je fait une erreur dans la syntaxe ou ma compréhension de LEFT OUTER JOIN est incomplète, j'espère que quelqu'un pourra expliquer comment cela pourrait se produire?

Postscript

Merci pour les bonnes réponses, ma compréhension des JOINTES EXTÉRIEURES GAUCHE est maintenant bien meilleure. Quelqu'un pourrait-il cependant suggérer un moyen de modifier cette requête afin que je ne reçoive que le nombre d'enregistrements renvoyés dans le tableau de gauche?

Cette requête sert uniquement à générer un rapport et les correspondances en double ne font que confondre les choses.

/ Postscript

Jay Wilde
la source
5
Pour "obtenir autant d'enregistrements renvoyés qu'il existe dans la table de gauche", vous devez spécifier quelle ligne à droite choisir s'il y a plusieurs correspondances.
AK
1
comment spécifiez-vous cela? J'aimerais que le premier match soit retourné.
Simon Cross le
1
vous devez définir ce que signifie la première correspondance. Voulez-vous le premier enregistrement, celui avec l'identifiant le plus élevé ou quoi?
HLGEM
1
Si vous correspondez avec la clé primaire dans la table supplémentaire, votre déclaration est correcte.
Prageeth godage
J'utilise souvent une ressource comme celle-ci comme aide- mémoire lors de la création de requêtes. Si le lien meurt, il suffit de rejoindre google sql ; ce sont des diagrammes de Venn des différents types de jointure.
Zimano le

Réponses:

190

Le LEFT OUTER JOIN renverra tous les enregistrements de la table LEFT joints à la table RIGHT lorsque cela est possible.

S'il y a des correspondances, il renverra toujours toutes les lignes qui correspondent, par conséquent, une ligne dans LEFT qui correspond à deux lignes dans RIGHT retournera comme deux ROWS, tout comme un INNER JOIN.

EDIT: En réponse à votre modification, je viens de jeter un coup d'œil à votre requête et il semble que vous ne renvoyez que les données de la table LEFT. Par conséquent, si vous ne voulez que des données de la table LEFT et que vous ne voulez qu'une seule ligne renvoyée pour chaque ligne de la table LEFT, vous n'avez pas du tout besoin d'effectuer un JOIN et pouvez simplement faire un SELECT directement à partir de la table LEFT.

Jour de Robin
la source
1
La raison pour laquelle j'ai rejoint la table de droite était que je n'ai obtenu que les enregistrements de la gauche où il y avait au moins un enregistrement dans la table de droite, mais merci beaucoup pour l'explication.
Jay Wilde
125
Table1                Table2
_______               _________
1                      2
2                      2
3                      5
4                      6

SELECT Table1.Id, Table2.Id FROM Table1 LEFT OUTER JOIN Table2 ON Table1.Id=Table2.Id

Résultats:

1,null
2,2
2,2
3,null
4,null
Andrew Lewis
la source
1
Si simple et pourtant si puissant.
kiradotee
39

Ce n'est pas impossible. Le nombre d'enregistrements dans le tableau de gauche est le nombre minimum d'enregistrements qu'il renverra. Si la table de droite contient deux enregistrements correspondant à un enregistrement de la table de gauche, elle renverra deux enregistrements.

HLGEM
la source
12

En réponse à votre post-scriptum, cela dépend de ce que vous souhaitez.

Vous obtenez (possible) plusieurs lignes pour chaque ligne de votre table de gauche car il existe plusieurs correspondances pour la condition de jointure. Si vous souhaitez que vos résultats totaux aient le même nombre de lignes que dans la partie gauche de la requête, vous devez vous assurer que vos conditions de jointure provoquent une correspondance 1-à-1.

Alternativement, selon ce que vous voulez réellement, vous pouvez utiliser des fonctions d'agrégation (si, par exemple, vous voulez juste une chaîne de la partie droite, vous pouvez générer une colonne qui est une chaîne délimitée par des virgules des résultats du côté droit pour cette ligne de gauche.

Si vous ne regardez que 1 ou 2 colonnes de la jointure externe, vous pouvez envisager d'utiliser une sous-requête scalaire car vous aurez la garantie d'un résultat.

Chris Cameron-Mills
la source
4
C'est une bonne réponse car elle offre des suggestions sur la façon de ne renvoyer que les lignes du tableau de gauche.
karns
9

Chaque enregistrement de la table de gauche sera renvoyé autant de fois qu'il y a d'enregistrements correspondants dans la table de droite - au moins 1, mais pourrait facilement être supérieur à 1.

Alex Martelli
la source
8

LEFT OUTER JOIN tout comme INNER JOIN (jointure normale) renverra autant de résultats pour chaque ligne de la table de gauche que de correspondances qu'il trouve dans la table de droite. Par conséquent, vous pouvez avoir beaucoup de résultats - jusqu'à N x M, où N est le nombre de lignes dans le tableau de gauche et M est le nombre de lignes dans le tableau de droite.

C'est que le nombre minimum de résultats est toujours garanti dans LEFT OUTER JOIN pour être au moins N.

excellent chef
la source
1
J'ai commencé à penser quand le nombre de lignes est égal à N x M et la seule situation réelle qui me vient à l'esprit est quand N ou M est égal à 1. Êtes-vous d'accord?
BartoszMiller
2
Non, non. Vous ne devriez pas considérer la condition de jointure comme une jointure d'égalité de clé uniquement. Il peut s'agir d'une condition arbitraire, par exemple des plages de dates, des inégalités, etc. Deux cas extrêmes: (a) N lignes n'ont pas une seule correspondance parmi M lignes, puis la jointure externe à gauche donne N lignes correspondant à NULL. (b) chacune des N lignes correspond à toutes les M lignes, alors le résultat est N x M lignes définies.
topchef du
1
Vous avez raison, je ne pensais aux jointures qu'en termes d'égalité clé. J'aime votre exemple du "cas b". Je crois que «toutes les N lignes correspondent à toutes les M lignes» est une recette générale pour le retour de N x M lignes, ce qui est plutôt impossible à visualiser en pensant uniquement à l'égalité des clés.
BartoszMiller
7

Serait-ce une relation un à plusieurs entre les tables de gauche et de droite?

Ken Burkhardt
la source
6

Faites attention si vous avez une clause where sur la table "côté droit" d'une requête contenant une jointure externe gauche ... Si vous n'avez pas d'enregistrement sur le côté droit satisfaisant la clause where, alors l'enregistrement correspondant du côté gauche 'table n'apparaîtra pas dans le résultat de votre requête ....

Serge
la source
1
Vous devez ensuite ajouter la condition à la clause ON de la jointure externe LEFT correspondante.
Mik
6

Si vous avez besoin d'une seule ligne du côté droit

SELECT SuspReason, SiteID FROM(
    SELECT SUSP.Susp_Visits.SuspReason, SUSP.Susp_Visits.SiteID, ROW_NUMBER()
    OVER(PARTITION BY SUSP.Susp_Visits.SiteID) AS rn
    FROM SUSP.Susp_Visits
    LEFT OUTER JOIN DATA.Dim_Member ON SUSP.Susp_Visits.MemID = DATA.Dim_Member.MembershipNum
) AS t
WHERE rn=1

ou juste

SELECT SUSP.Susp_Visits.SuspReason, SUSP.Susp_Visits.SiteID
FROM SUSP.Susp_Visits WHERE EXISTS(
    SELECT DATA.Dim_Member WHERE SUSP.Susp_Visits.MemID = DATA.Dim_Member.MembershipNum
)
AK
la source
1
Parce que vous n'avez pas fourni DDL et DML, je n'ai pas testé. Quoi qu'il en soit, je pense que EXISTS est ce que vous voulez. Essayez ceci: SELECT SuspReason, SiteID FROM (SELECT SUSP.Susp_Visits.SuspReason, SUSP.Susp_Visits.SiteID, ROW_NUMBER () OVER (PARTITION BY SUSP.Susp_Visits.SiteID ORDER BY SUSP.Susp_Visits.SiteID) AS rn FROM SUSP.Susp.SiteID REJOIGNEZ DATA.Dim_Member ON SUSP.Susp_Visits.MemID = DATA.Dim_Member.MembershipNum) AS t WHERE rn = 1
AK
2

Il semble qu'il y ait plusieurs lignes dans la table DATA.Dim_Member par ligne SUSP.Susp_Visits.

bdukes
la source
2

si plusieurs (x) lignes dans Dim_Member sont associées à une seule ligne dans Susp_Visits, il y aura x lignes dans l'ensemble de résultats.

Manu
la source