2010-12-27 14 views
4

Ich stieß auf dieses interessante Verhalten. Ich sehe Links-Join ist der Weg zu gehen, würde aber immer noch gerne dies gelöscht haben. Ist es ein Bug oder Verhalten By-Design? Irgendwelche Erklärungen?SQL SELECT mit "IN" Unterabfrage gibt keine Datensätze zurück, wenn die Unterabfrage NULL enthält

Wenn ich Datensätze aus der linken Tabelle auswähle, bei denen im Ergebnis einer Unterabfrage in der rechten Tabelle kein Wert vorhanden ist, wird der erwartete "fehlende" Datensatz nicht zurückgegeben, wenn das Ergebnis der Unterabfrage Nullen enthält. Ich habe erwartet, dass die beiden Möglichkeiten, diese Abfrage zu schreiben, gleichwertig sind.

Danke!

declare @left table (id int not null primary key identity(1,1), ref int null) 
declare @right table (id int not null primary key identity(1,1), ref int null) 

insert @left (ref) values (1) 
insert @left (ref) values (2) 

insert @right (ref) values (1) 
insert @right (ref) values (null) 

print 'unexpected empty resultset:' 
select * from @left 
where ref not in (select ref from @right) 

print 'expected result - ref 2:' 
select * from @left 
where ref not in (select ref from @right where ref is not null) 

print 'expected result - ref 2:' 
select l.* from @left l 
    left join @right r on r.ref = l.ref 
where r.id is null 

print @@version 

gibt:

(1 row(s) affected) 

(1 row(s) affected) 

(1 row(s) affected) 

(1 row(s) affected) 
unexpected empty resultset: 
id   ref 
----------- ----------- 

(0 row(s) affected) 

expected result - ref 2: 
id   ref 
----------- ----------- 
2   2 

(1 row(s) affected) 

expected result - ref 2: 
id   ref 
----------- ----------- 
2   2 

(1 row(s) affected) 

Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64) 
    Apr 2 2010 15:48:46 
    Copyright (c) Microsoft Corporation 
    Standard Edition (64-bit) on Windows NT 6.0 <X64> (Build 6002: Service Pack 2) (Hypervisor) 

Antwort

6

Das ist von Entwurf. Wenn die Übereinstimmung fehlschlägt und die Menge NULL enthält, ist das Ergebnis NULL, wie vom SQL-Standard angegeben.

 
'1' IN ('1', '3') => true 
'2' IN ('1', '3') => false 
'1' IN ('1', NULL) => true 
'2' IN ('1', NULL) => NULL 

'1' NOT IN ('1', '3') => false 
'2' NOT IN ('1', '3') => true 
'1' NOT IN ('1', NULL) => false 
'2' NOT IN ('1', NULL) => NULL 

Informell, die Logik dahinter ist, dass NULL als ein unbekannter Wert gedacht werden kann. Zum Beispiel ist es hier egal, was der unbekannte Wert ist - "1" ist eindeutig in der Menge, so dass das Ergebnis wahr ist.

'1' IN ('1', NULL) => true 

Im folgende Beispiel können wir nicht sicher sein, dass ‚2‘ in dem Satz ist, aber da wir nicht alle Werte wissen wir auch nicht sicher sein, dass es ist nicht im Set. Also das Ergebnis ist NULL.

'2' IN ('1', NULL) => NULL 

Ein anderer Weg, es zu betrachten ist durch x NOT IN (Y, Z) als X <> Y AND X <> Z neu zu schreiben. Dann können Sie die Regeln der three-valued logic verwenden:

true AND NULL => NULL 
false AND NULL => false 
+0

-Design? Ist das Wort Zustimmung nicht? –

+0

Sehr gut gemacht. Vielen Dank! – Rbjz

0

Das ist die Art und Weise ist das ANSI-Komitee denkt getan werden müssen.

Sie können Ihre Fragen mit

set ansi_defaults OFF 

vorangehen und Sie das Ergebnis zu erhalten, die Sie erwarten.

Seit SQL-Server 7.0 ist Microsoft ziemlich streng auf die Einhaltung der ANSI-Standards.

EDIT:

Kämpfe nicht gegen die Standardwerte. Du wirst am Ende aufgeben.

+0

hahaaha nein Ich werde nicht die Standards für sicher kämpfen! – Rbjz

3

Ja, so wurde es entworfen. Es gibt auch viele andere Überlegungen zwischen einer LEFT JOIN oder einer NOT IN. Sie sollten dieses link sehen, um eine sehr gute Erklärung dieses Verhaltens zu haben.

+0

Ich mochte den Artikel, zu dem Sie verlinken. Einfach zu folgen und erklärt sehr gut Unterschied zwischen NOT IN/NOT EXISTS/LEFT JOIN. Vielen Dank! Nun, wenn der Autor es erweitern könnte, um die umgekehrten Fälle (IN/EXISTS/EXCEPT (SQL 2005+)) aufzunehmen, wäre es wirklich großartig. –

+0

@Joe Pineda Ja, es ist ein toller Artikel. Ich wünschte, ich hätte mehr Verdienst als nur gefunden, aber .... – Lamak

+0

Hallo, danke für den Artikel Link, das war sehr hilfreich. Obwohl ich Marks Antwort wähle, da sie eine Inline-Erklärung hat. Robert – Rbjz

0

Die Ursache des Verhaltens wird von Mark erklärt. Es kann auf mehrere Arten aufgelöst werden: - LINKEN VERBINDEN, Filtern von NULL-Werten aus der inneren Abfrage durch Ausfiltern von where-Klausel ODER aus select-Klausel, Verwenden einer zusammengehörenden Unterabfrage - um nur einige zu nennen.

Nach drei kurzen Pfosten wird eine Fallstudie zum gleichen Thema: - NOT IN Subquery return zero rows -Issue, NOT IN Subquery return zero rows -Root Cause, NOT IN Subquery return zero rows -Workarounds