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
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.
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
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.
Cela fait une différence, car dans le second cas, vous appliquez l’endroit APRÈS la jointure gauche.
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'