web-dev-qa-db-fra.com

Pourquoi et quand une LEFT JOIN avec une condition dans la clause WHERE n'est pas équivalente à la même une LEFT JOIN dans ON?

Je rencontre une situation très déroutante qui me fait remettre en question toute ma compréhension des jointures dans SQL Server.

SELECT t1.f2 
FROM   t1 
LEFT JOIN t2 
ON t1.f1 = t2.f1 AND cond2 AND t2.f3 > something 

Ne donne pas les mêmes résultats que:

SELECT t1.f2 
FROM   t1 
LEFT JOIN t2 
ON t1.f1 = t2.f1 AND cond2 
WHERE  t2.f3 > something 

S'il vous plaît, aidez quelqu'un en indiquant si ces deux requêtes sont supposées être équivalentes ou non.

THX

18
Kamel Keb

La clause on est utilisée lorsque join recherche des lignes correspondantes. La clause where est utilisée pour filtrer les lignes une fois que toutes les jointures ont été effectuées.

Un exemple avec Disney toons votant pour le président:

declare @candidates table (name varchar(50));
insert @candidates values 
    ('Obama'), 
    ('Romney');
declare @votes table (voter varchar(50), voted_for varchar(50));
insert @votes values 
    ('Mickey Mouse', 'Romney'),
    ('Donald Duck', 'Obama');

select  *
from    @candidates c
left join    
        @votes v
on      c.name = v.voted_for
        and v.voter = 'Donald Duck'

Ceci retourne toujours Romney même si Donald n'a pas voté pour lui. Si vous déplacez la condition de la variable on vers la clause where:

select  *
from    @candidates c
left join    
        @votes v
on      c.name = v.voted_for
where   v.voter = 'Donald Duck'

Romney ne sera plus dans le jeu de résultats.

32
Andomar

Les deux sont littéralement différents.

La première requête effectue le filtrage de la table t2 avant la réunion des tables. Ainsi, les résultats seront ensuite joints à la table t1, de sorte que tous les enregistrements de t1 seront affichés dans la liste.

Le second filtre du résultat total après la jointure des tables.


Voici un exemple

Tableau 1

ID   Name
1    Stack
2    Over 
3    Flow

Tableau 2

T1_ID   Score
1       10
2       20
3       30

Dans votre première requête, cela ressemble à ceci,

SELECT  a.*, b.Score
FROM    Table1 a
        LEFT JOIN Table2 b
           ON a.ID = b.T1_ID AND
              b.Score >= 20

Avant de joindre les tables, les enregistrements de table2 sont d'abord filtrés par la partition. Donc, les seuls enregistrements qui seront joints à la table1 sont

T1_ID   Score
2       20
3       30

parce que la Score de T1_ID est seulement 10. Le résultat de la requête est 

ID   Name    Score
1    Stack   NULL
2    Over    20
3    Flow    30

Alors que la deuxième requête est différente.

SELECT  a.*, b.Score
FROM    Table1 a
        LEFT JOIN Table2 b
           ON a.ID = b.T1_ID
WHERE   b.Score >= 20

Il joint les enregistrements en premier, qu'il ait ou non un enregistrement correspondant sur l'autre table. Donc, le résultat sera

ID   Name    Score
1    Stack   10
2    Over    20
3    Flow    30

et le filtrage a lieu b.Score >= 20. Donc, le résultat final sera

ID   Name    Score
2    Over    20
3    Flow    30
17
John Woo

Dans le premier cas, les résultats dans t2 sont filtrés dans le cadre de la jointure.

Dans le second cas, il pourrait y avoir plus de lignes disponibles à partir de t2

Essentiellement, l'ensemble des enregistrements joints aux deux requêtes ne sera pas le même.

0
Ryan

Cela fait une différence, car dans le second cas, vous appliquez l’endroit APRÈS la jointure gauche.

0
Brian
CREATE TABLE Company
(
CompanyId TinyInt Identity Primary Key,
CompanyName Nvarchar(50) NULL
)
GO

INSERT Company VALUES('Dell')
INSERT Company VALUES('HP')
INSERT Company VALUES('IBM')
INSERT Company VALUES('Microsoft')
GO

CREATE TABLE Candidate
(
CandidateId tinyint identity primary key,
FullName nvarchar(50) NULL,
CompanyId tinyint REFERENCES Company(CompanyId)
)
GO

INSERT Candidate VALUES('Ron',1)
INSERT Candidate VALUES('Pete',2)
INSERT Candidate VALUES('Steve',3)
INSERT Candidate VALUES('Steve',NULL)
INSERT Candidate VALUES('Ravi',1)
INSERT Candidate VALUES('Raj',3)
INSERT Candidate VALUES('Kiran',NULL)
GO

SELECT * from Company c
SELECT * from Candidate c

-- A simple left outer Join
SELECT * FROM Company c LEFT OUTER JOIN Candidate c2
ON c.CompanyId = c2.CompanyId

--Left Outer Join ON and AND condition fetches 5 rows wtih NULL value from right side table 
SELECT * FROM Company c LEFT OUTER JOIN Candidate c2
ON c.CompanyId = c2.CompanyId
AND c.CompanyName = 'Dell' 

--Left Outer Join ON and where clause fetches only required rows
SELECT * FROM Company c LEFT OUTER JOIN Candidate c2
ON c.CompanyId = c2.CompanyId
AND c.CompanyName = 'Dell' 
WHERE c.CompanyName='IBM'
0
mr_eclair