2013-03-29 11 views
18

Sto vivendo una situazione molto confusa che mi mette in dubbio tutta la mia comprensione dei join in SQL Server.Perché e quando un LEFT JOIN con condizioni nella clausola WHERE non è equivalente allo stesso LEFT JOIN in ON?

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

non dà gli stessi risultati:

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

può piacere a qualcuno aiuto dicendo se queste due query dovrebbero essere equivalenti o no?

Thx

+2

Il 'ON' riduce l'insieme di righe che si uniscono, mentre il' WHERE' riduce l'insieme di righe che vengono aggiunti al set di risultati ... – Lucas

+2

Essi non sono equivalenti. La prima query filtra i risultati nel join; i secondi filtri * dopo *. –

+0

Non sono sicuro che le domande siano equivalenti perché qui, la condizione è espressa sul secondo tavolo .. –

risposta

30

La clausola on viene utilizzato quando il join è alla ricerca di righe corrispondenti. La clausola where viene utilizzata per filtrare le righe dopo che è stata eseguita l'unione.

Un esempio con Toon Disney che votano per il presidente:

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' 

Questo restituisce comunque Romney anche se Donald non ha votato per lui. Se si sposta la condizione dal on alla clausola where:

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

Romney non sarà più nel set di risultati.

+1

Si noti che questo esempio utilizza un join OUTER, che è il caso in cui questi 2 frammenti di codice non sono equivalenti dal punto di vista funzionale. –

+0

Stavo cercando qualcosa di completamente diverso, ma in qualche modo google mi ha portato qui e le tue prime due frasi mi hanno salvato la giornata! Grazie +1! – ksno

0

Nel primo caso, sono risultati in t2 si filtra come parte della giunzione.

Nel secondo caso, potrebbero essere disponibili più righe da t2.

In sostanza, l'insieme di record aggiunti nelle due query non sarà lo stesso.

0

Si fa differenza perché in secondo caso si sta applicando il dove, dopo lo fa il join sinistro

17

Entrambi sono letteralmente diverso.

La prima query esegue il filtraggio della tabella t2 prima dell'avvento delle tabelle. Quindi i risultati saranno quindi uniti sul tavolo t1 in modo che tutti i record di t1 vengano visualizzati nell'elenco.

Il secondo filtra dal risultato totale dopo l'unione delle tabelle.


Ecco un esempio

Table1

ID Name 
1 Stack 
2 Over 
3 Flow 

Table2

T1_ID Score 
1  10 
2  20 
3  30 

Nella tua prima domanda, sembra che questo,

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

Quello che fa è prima di unire le tabelle, i record di table2 vengono filtrati prima dal punteggio. Quindi l'unico record che verranno uniti su table1 sono

T1_ID Score 
2  20 
3  30 

perché il Score di T1_ID dista solo 10.Il risultato dell'interrogazione è

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

Mentre la seconda query è diverso.

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

Si unisce prima ai record se ha un record corrispondente sull'altro o meno. Così il risultato sarà

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

e il filtraggio avviene b.Score >= 20. Così il risultato finale sarà

ID Name Score 
2 Over 20 
3 Flow 30 
+0

Ben spiegato! –

0
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'