2010-05-28 3 views
6

Ich versuche, zwei Tabellen zu vergleichen, um Zeilen in jeder Tabelle zu finden, die nicht in der anderen Tabelle enthalten sind. Tabelle 1 enthält eine groupby -Spalte, um zwei Datensätze innerhalb der ersten Tabelle zu erstellen.T-SQL - Linke äußere Verknüpfungen - Filtert die where-Klausel im Vergleich zur on-Klausel

groupby  number 
----------- ----------- 
1   1 
1   2 
2   1 
2   2 
2   4 

Tabelle 2 hat nur eine Spalte.

number 
----------- 
1 
3 
4 

Also Tabelle 1 hat die Werte 1,2,4 in Gruppe 2 und Tabelle 2 hat die Werte 1,3,4.

Ich erwarte, dass das folgende Ergebnis, wenn für die Gruppe Beitritt 2:

`Table 1 LEFT OUTER Join Table 2` 
T1_Groupby T1_Number T2_Number 
----------- ----------- ----------- 
2   2   NULL 

`Table 2 LEFT OUTER Join Table 1` 
T1_Groupby T1_Number T2_Number 
----------- ----------- ----------- 
NULL  NULL  3 

Die einzige Art, wie ich dies an der Arbeit bekommen kann, ist, wenn ich eine where-Klausel für das erste beitreten setzen:

PRINT 'Table 1 LEFT OUTER Join Table 2, with WHERE clause' 
select table1.groupby as [T1_Groupby], 
     table1.number as [T1_Number], 
     table2.number as [T2_Number] 
from table1 
     LEFT OUTER join table2 
     --****************************** 
     on table1.number = table2.number 
     --****************************** 
WHERE table1.groupby = 2 
    AND table2.number IS NULL 

und ein Filter in der ON für die zweite:

PRINT 'Table 2 LEFT OUTER Join Table 1, with ON clause' 
select table1.groupby as [T1_Groupby], 
     table1.number as [T1_Number], 
     table2.number as [T2_Number] 
from table2 
     LEFT OUTER join table1 
      --****************************** 
      on table2.number = table1.number 
      AND table1.groupby = 2 
      --****************************** 
WHERE table1.number IS NULL 

Kann jemand mit einem Weg kommen, den Filter nicht zu verwenden in der on-Klausel, aber in der where-Klausel?

Der Kontext davon ist, ich habe einen Staging-Bereich in einer Datenbank und ich möchte neue Datensätze und Datensätze identifizieren, die gelöscht wurden. Das groupby-Feld entspricht einer Batchid für einen Extrakt, und ich vergleiche den neuesten Extrakt in einer temporären Tabelle mit dem in einer partioneds-Tabelle gespeicherten Batch von gestern, der auch alle zuvor extrahierten Batches enthält. Code erstellen Tabelle 1 und 2:

create table table1 (number int, groupby int) 
create table table2 (number int) 
insert into table1 (number, groupby) values (1, 1) 
insert into table1 (number, groupby) values (2, 1) 
insert into table1 (number, groupby) values (1, 2) 
insert into table2 (number) values (1) 
insert into table1 (number, groupby) values (2, 2) 
insert into table2 (number) values (3) 
insert into table1 (number, groupby) values (4, 2) 
insert into table2 (number) values (4) 

EDIT:

Ein bisschen mehr Kontext - je nachdem, wo ich legte den Filter ich unterschiedliche Ergebnisse. Wie oben erwähnt, gibt mir die Where-Klausel das korrekte Ergebnis in einem Zustand und das ON im anderen Zustand. Ich bin auf der Suche nach einem konsequenten Weg, dies zu tun.

Wo -

select table1.groupby as [T1_Groupby], 
     table1.number as [T1_Number], 
     table2.number as [T2_Number] 
from table1 
     LEFT OUTER join table2 
      --****************************** 
      on table1.number = table2.number 
      --****************************** 
WHERE table1.groupby = 2 
    AND table2.number IS NULL 

Ergebnis:

T1_Groupby T1_Number T2_Number 
----------- ----------- ----------- 
2   2   NULL 

On -

select table1.groupby as [T1_Groupby], 
     table1.number as [T1_Number], 
     table2.number as [T2_Number] 
from table1 
     LEFT OUTER join table2 
      --****************************** 
      on table1.number = table2.number 
      AND table1.groupby = 2 
      --****************************** 
WHERE table2.number IS NULL 

Ergebnis:

T1_Groupby T1_Number T2_Number 
----------- ----------- ----------- 
1   1   NULL 
2   2   NULL 
1   2   NULL 

Wo (Tabelle 2 diesmal) -

select table1.groupby as [T1_Groupby], 
     table1.number as [T1_Number], 
     table2.number as [T2_Number] 
from table2 
     LEFT OUTER join table1 
      --****************************** 
      on table2.number = table1.number 
      AND table1.groupby = 2 
      --****************************** 
WHERE table1.number IS NULL 

Ergebnis:

T1_Groupby T1_Number T2_Number 
----------- ----------- ----------- 
NULL  NULL  3 

On -

select table1.groupby as [T1_Groupby], 
     table1.number as [T1_Number], 
     table2.number as [T2_Number] 
from table2 
     LEFT OUTER join table1 
      --****************************** 
      on table2.number = table1.number 
      --****************************** 
WHERE table1.number IS NULL 
    AND table1.groupby = 2 

Ergebnis:

T1_Groupby T1_Number T2_Number 
----------- ----------- ----------- 
(0) rows returned 
+1

Warum ist es wichtig, die Bedingung von der 'JOIN'-Klausel in die' WHERE'-Klausel zu verschieben? Das Setzen von Prädikaten in 'JOIN' ist normal, wenn Sie diese Art von Verhalten wünschen. – Aaronaught

+0

Das Verschieben in die where-Klausel ist nicht so wichtig wie die Tatsache, dass ich für den ersten Join, bei dem ich Tabelle 1 mit Tabelle 2 verbinde, ein anderes Ergebnis erhalte, wenn ich den Filter in on und nicht in where lege. PRINT 'Tabelle 1 LEFT OUTER JOIN Tabelle 2, mit WHERE-Klausel' \t table1.groupby als [T1_Groupby] wählen , \t \t table1.number als [T1_Number], \t \t table2.number als [T2_Number] aus \t tabelle1 \t \t LEFT OUTER JOIN table2 \t \t \t - ****************************** \t \t \t auf table1.number = table2.number \t \t \t UND table1.groupby = 2 \t \t \t - ****************************** WHERE \t --table1 .groupby = 2 UND \t table2.number IS NULL Gibt null nur –

+0

Nun, welche wollen Sie? Es klingt wie der NULL-Join, was du meinst. Dies ist eine Standardmethode für Abfragen wie diese und ist in der Regel den Unterabfragenalternativen vorzuziehen. Ich sehe keinen Vorteil darin, die Bedingung in eine WHERE-Klausel zu schieben. – bobince

Antwort

11

Wenn Sie den linken äußeren Join-Tabelle in der WHERE-Klausel filtern, dann sind Sie in der Tat die Schaffung eines INNER JOIN

Siehe auch diese Wiki-Seite: WHERE conditions on a LEFT JOIN

+0

Danke für den Wiki-Link. –

5

mit LEFT OUTER JOINS, müssen Sie Filter in der ON-Klausel oder verwenden Sie diese:

WHERE 
    (LeftJoinTable.ID IS NULL OR LeftJoinTable.Col1=YourFilter) 

, wenn Sie nur in der WHERE-Filter:

WHERE 
    LeftJoinTable.Col1=YourFilter 

Sie erhalten die Eltern verwerfen Reihe verbinden, wenn es keine LeftJoinTable.ID ist (so dass die eine INNER JOIN verbinden).

Indem Sie den Filter in den ON-Modus setzen, bewirken Sie die Eliminierung der LINKEN JOIN-Zeile, aber nicht die Eliminierung der übergeordneten Join-Zeile, so funktioniert es.

EDIT Basis don OP Kommentar
der einzige Weg, eine ein LEFT OUTER JOIN Tabelle in der ON-Klausel zu filtern ist, wenn Sie eine OR verwenden möchten, wie ich oben in dem ersten Codebeispiel zeigen. Es ist nichts falsch, einen LINKEN ÄUSSEREN JOIN in der ON-Klausel zu filtern, so wird es gemacht.

+0

Wahrscheinlich sollte ich (im Nachhinein die perfekte Wissenschaft) in die ursprüngliche Frage bringen, aber wenn Sie sich meinen obigen Kommentar ansehen, werden Sie sehen, dass das Ändern des Filters von der Stelle, an der die Verbindung zuerst kommt, zu einem anderen Ergebnis führt. Ich suche nach einer "Regel", wenn ich äußere Links verweise, die ich entweder ON-Klauseln oder WHERE-Klauseln verwenden soll. –

2

Da die Abfrage geschrieben ist, macht es Sinn, stellen die Verbindung in der oN-Klausel, da Sie speziell nur wollen auf Werte in Gruppe beizutreten ‚2‘ aus der Tabelle 1.

die Alternative ist, zu Vorfilter tabelle1 zur Gruppe Sie interessiert sind, wie dies

select t1Group.groupby, 
     t1Group.number as [T1_Number], 
     table2.number as [T2_Number] 
from table2 
     LEFT OUTER join (SELECT * FROM table1 WHERE groupby=2) t1Group 
      on table2.number = t1Group.number 
WHERE t1Group.number IS NULL 
+0

+1 der Schlüssel zu verstehen, warum Filterung in der "ON" -Klausel funktioniert, ist den Filter in eine Inline-View wie folgt zu nehmen. – araqnid

0
SELECT dbo.table1.groupby as [T1_Groupby], 
     dbo.table1.number as [T1_Number], 
     t21.number as [t21_Number] 
FROM dbo.table1 
LEFT OUTER join dbo.table2 t21 
    ON dbo.table1.number = t21.number 
LEFT OUTER join dbo.table2 t22 
    ON dbo.table1.groupby= t22.number 
WHERE t21.number is null AND t22.number is null 
0
select dbo.table1.groupby as [T1_Groupby], 
          dbo.table1.number as [T1_Number], 
          t22.number as [t22_Number] 

        from dbo.table1 right outer join 
        (select dbo.table1.groupby, 
          dbo.table2.number as number 

        from dbo.table1 
        right OUTER join dbo.table2 
        on dbo.table1.number = dbo.table2.number 

        where dbo.table1.number is null) t22 
        on dbo.table1.groupby = t22.number 
        where dbo.table1.groupby is null 
0

Ich habe mit diesem selbst zu kämpfen - und am Ende des Tages war es, Daten aus der Tabelle auszuwählen, die mit Where-Klausel und es in eine temporären Tabelle zu setzen, und dann LEFT OUTER auf dem Temp verbinden verwenden Tabelle.

SELECT table1.GroupBy, table1.number INTO #Temp FROM table1 WHere GroupBy = 2 
SELECT table2.Groupby, #temp.number From table2 LEFT OUTER JOIN #temp on table2.Groupby = #temp.Groupby 
Verwandte Themen