Frage

Dieses Problem trat auf, wenn ich verschiedene Aufzeichnungen zählt für bekam, was ich dachte identische Anfragen ein mit einem not in where Zwang und die andere eine left join waren. Die Tabelle in der not in Einschränkung hatte einen Nullwert (fehlerhafte Daten), die die Abfrage verursachte eine Zählung von 0 Datensätze zurück. Ich irgendwie verstehen, warum, aber ich könnte etwas Hilfe voll nutzen das Konzept zu begreifen.

Um es zu sagen, einfach, warum Abfrage A ein Ergebnis zurück, aber B nicht?

A: select 'true' where 3 in (1, 2, 3, null)
B: select 'true' where 3 not in (1, 2, null)

Dies war auf SQL Server 2005. Ich fand auch, dass set ansi_nulls off Aufruf bewirkt, dass B ein Ergebnis zurück.

War es hilfreich?

Lösung

Abfrage A ist die gleiche wie:

select 'true' where 3 = 1 or 3 = 2 or 3 = 3 or 3 = null

Da 3 = 3 wahr ist, erhalten Sie ein Ergebnis aus.

Abfrage B ist die gleiche wie:

select 'true' where 3 <> 1 and 3 <> 2 and 3 <> null

Wenn ansi_nulls eingeschaltet ist, 3 <> null ist unbekannt, so das Prädikat ausgewertet UNKNOWN, und Sie erhalten keine Zeilen.

Wenn ansi_nulls ausgeschaltet ist, 3 <> null wahr ist, so das Prädikat wahr ergibt, und Sie erhalten eine Zeile.

Andere Tipps

Wenn Sie NULL verwenden Sie wirklich mit einer dreiwertigen Logik zu tun.

Ihre erstes Abfrage gibt die Ergebnisse als die WHERE-Klausel ausgewertet:

    3 = 1 or 3 = 2 or 3 = 3 or 3 = null
which is:
    FALSE or FALSE or TRUE or UNKNOWN
which evaluates to 
    TRUE

Die zweite:

    3 <> 1 and 3 <> 2 and 3 <> null
which evaluates to:
    TRUE and TRUE and UNKNOWN
which evaluates to:
    UNKNOWN

Das Unbekannte ist nicht das gleiche wie FALSCH Sie können es einfach testen, indem Aufruf:

select 'true' where 3 <> null
select 'true' where not (3 <> null)

Beide Abfragen geben Ihnen keine Ergebnisse

Wenn die UNKNOWN war die gleiche wie FALSCH dann davon aus, dass die erste Abfrage Sie FALSCH die zweite auf TRUE würde bewerten geben würde, da es die gleiche gewesen wäre als nicht (false).
Das ist nicht der Fall.

Es ist ein sehr guter Artikel zu diesem Thema auf SqlServerCentral .

Die ganze Frage der NULL-Werte und dreiwertigen Logik kann ein wenig verwirrend auf den ersten, aber es ist wichtig, zu verstehen, um korrekte Abfragen in TSQL

zu schreiben

Ein weiterer Artikel, den ich empfehlen würde, ist SQL Aggregatfunktionen und NULL .

NOT IN gibt 0 Datensätze verglichen, wenn sie gegen einen unbekannten Wert

Da NULL ist eine unbekannte, eine NOT IN Abfrage eine NULL oder NULLs in der Liste der möglichen Werte enthalten, werden immer 0 Datensätze zurück, da es keine Möglichkeit gibt, um sicher zu sein, dass der NULL Wert ist nicht der Wert geprüft wird.

auf null vergleichen ist nicht definiert, es sei denn, Sie NULL verwendet werden.

Also, wenn 3 bis NULL (Abfrage A) zu vergleichen, gibt sie nicht definiert.

d. SELECT 'true', wo in 3 (1,2, null)  und SELECT 'true', wo 3 nicht in (1,2, null)

das gleiche Ergebnis, wie NOT (nicht definiert) noch nicht definiert ist, aber nicht TRUE

Der Titel dieser Frage zum Zeitpunkt des Schreibens ist

  

SQL NICHT IN Einschränkung und NULL-Werte

Aus dem Text der Frage scheint es, dass das Problem in einer SQL-DML SELECT Abfrage auftritt, wurde anstelle einer SQL DDL CONSTRAINT.

jedoch insbesondere den Wortlaut des Titels gegeben, mag ich darauf hinweisen, dass einige Aussagen hier sind potenziell irreführende Angaben gemacht, die entlang der Linien von (paraphrasieren)

  

Wenn das Prädikat als UNKNOWN bekommt man nicht alle Zeilen.

Auch wenn dies der Fall für SQL DML ist, wenn man bedenkt Einschränkungen der Effekt ist anders.

Betrachten Sie diese sehr einfache Tabelle mit zwei direkt von den Prädikaten in der Frage getroffen Zwänge (und adressiert in einer ausgezeichneten Antwort von @Brannon):

DECLARE @T TABLE 
(
 true CHAR(4) DEFAULT 'true' NOT NULL, 
 CHECK ( 3 IN (1, 2, 3, NULL )), 
 CHECK ( 3 NOT IN (1, 2, NULL ))
);

INSERT INTO @T VALUES ('true');

SELECT COUNT(*) AS tally FROM @T;

Wie pro @ Brannon Antwort, die erste Einschränkung (mit IN) dem Wert TRUE und die zweite Bedingung (mit NOT IN) ausgewertet UNKNOWN. Doch , der Einsatz gelingt! Daher wird in diesem Fall ist es nicht ganz richtig zu sagen: „Sie bekommen keine Reihen“, weil wir in der Tat eine Reihe eingeführt als Ergebnis bekommen haben.

Der obige Effekt ist in der Tat der richtige, wie der SQL-92-Standard betrifft. Vergleichen und den folgenden Abschnitt aus der SQL-92-Spezifikation

  

7.6 where-Klausel

     

Das Ergebnis der eine Tabelle mit diesen Zeilen von T für   die das Ergebnis der Suchbedingung wahr ist.

     

4.10 Integritätsbedingungen

     

Eine Tabelle Check-Bedingung erfüllt ist, wenn und nur wenn der angegebene   Suchbedingung ist für jede Zeile einer Tabelle nicht falsch.

Mit anderen Worten:

In SQL DML werden Zeilen aus dem Ergebnis entfernt, wenn der WHERE zu UNKNOWN, weil es nicht die Bedingung "wahr".

In SQL DDL (das heißt constraints), Zeilen nicht aus dem Ergebnis entfernt, wenn sie zu UNKNOWN ausgewertet werden, weil es hat die Bedingung "nicht falsch".

Obwohl die Auswirkungen in SQL DML und SQL DDL bzw. widersprüchlich erscheinen mag, gibt es praktische Grund für UNKNOWN Ergebnisse des ‚in dubio pro reo‘ zu geben, indem sie eine Einschränkung erfüllen (mehr richtig, so dass sie nicht scheitern zu befriedigen eine Einschränkung): ohne dieses Verhalten würden alle Einschränkungen explizit Nullen behandeln müssen und das wäre von einer Sprache gestalterischen Sicht sehr unbefriedigend sein (kein Recht Schmerz für Programmierer zu erwähnen,)

P. S. wenn Sie es als eine Herausforderung zu finden sind solche Logik zu folgen, wie „unbekannt keinen Zwang nicht scheitern zu erfüllen“, wie ich es schreiben bin, betrachten, dann können Sie mit all dies verzichten einfach durch Nullable-Spalten in SQL DDL und alles, was in SQL DML vermeiden dass produziert nulls (zB Outer-Joins)!

A, 3 auf Gleichheit mit jedem Mitglied des Satzes getestet, wodurch man (FALSE, FALSE, TRUE, UNKNOWN). Da eines der Elemente wahr ist, ist die Bedingung erfüllt. (Es ist auch möglich, dass einige Kurzschlüsse hier stattfinden, so dass es tatsächlich stoppt, sobald er die erste TRUE trifft und werten nie 3 = NULL.)

In B, ich denke, es ist die Bedingung Auswertung als NOT (3 in (1,2, null)). Testen 3 auf Gleichheit mit den eingestellten Ausbeuten (FALSE, FALSE, unbekannt), die an UNKNOWN aggregiert. NOT (UNKNOWN) ergibt UNKNOWN. Insgesamt also die Wahrheit der Erkrankung ist unbekannt, die am Ende im wesentlichen als FALSCH behandelt wird.

Null bedeutet und das Fehlen von Daten, das ist es nicht bekannt ist, kein Datenwert von nichts. Es ist sehr einfach für die Menschen von einem Programmier Hintergrund, weil in den Sprachen C-Typs zu verwirren, wenn Zeiger null verwendet, ist in der Tat nichts.

daher im ersten Fall 3 ist in der Tat in dem Satz von (1,2,3, null) ist so wahr zurück

In der zweiten jedoch können Sie es reduzieren

wählen 'true', wo 3 nicht in (null)

Also nichts zurückgegeben, da der Parser nichts über den Satz kennt, zu der Sie es vergleichen - es ist nicht eine leere Menge, sondern eine unbekannte Set. (1, 2, null) hilft nicht, weil der (1,2) Satz offensichtlich falsch ist, aber dann, dass Sie gegen unbekannt-Verknüpfungs, die unbekannt ist.

Es kann aus den Antworten geschlossen werden hier, dass NOT IN (subquery) nicht behandelt nulls richtig und sollte für NOT EXISTS vermieden werden. Allerdings ist eine solche Schlussfolgerung verfrüht sein kann. Im folgende Szenario zu Chris Date (Datenbank Programmierung und Design, Band 2 Nr 9, September 1989) gutgeschrieben, ist es NOT IN, die richtig behandelt Nullen und gibt das richtige Ergebnis, anstatt NOT EXISTS.

Betrachten Sie eine Tabelle sp Lieferanten zu vertreten (sno), die bekannt sind Teile (pno) in der Menge (qty) zu liefern. Die Tabelle hält derzeit die folgenden Werte:

      VALUES ('S1', 'P1', NULL), 
             ('S2', 'P1', 200),
             ('S3', 'P1', 1000)

Beachten Sie, dass Quantität auf NULL festlegbare das heißt in der Lage sein, die Tatsache zu erfassen ein Lieferant bekannt Teile selbst zu liefern, wenn es nicht in welcher Menge bekannt ist.

Die Aufgabe ist es, die Lieferanten zu finden, die Versorgungsteilnummer ‚P1‘ bekannt sind, aber nicht in Mengen von 1000.

In der folgenden verwendet NOT IN richtig Lieferanten S2 'nur zu identifizieren:

WITH sp AS 
     ( SELECT * 
         FROM ( VALUES ( 'S1', 'P1', NULL ), 
                       ( 'S2', 'P1', 200 ),
                       ( 'S3', 'P1', 1000 ) )
              AS T ( sno, pno, qty )
     )
SELECT DISTINCT spx.sno
  FROM sp spx
 WHERE spx.pno = 'P1'
       AND 1000 NOT IN (
                        SELECT spy.qty
                          FROM sp spy
                         WHERE spy.sno = spx.sno
                               AND spy.pno = 'P1'
                       );

jedoch die folgende Abfrage die gleiche allgemeine Struktur, aber mit NOT EXISTS nutzt jedoch nicht einwandfrei schließt Lieferanten S1 'in dem Ergebnis (d.h., für die die Menge ist null):

WITH sp AS 
     ( SELECT * 
         FROM ( VALUES ( 'S1', 'P1', NULL ), 
                       ( 'S2', 'P1', 200 ),
                       ( 'S3', 'P1', 1000 ) )
              AS T ( sno, pno, qty )
     )
SELECT DISTINCT spx.sno
  FROM sp spx
 WHERE spx.pno = 'P1'
       AND NOT EXISTS (
                       SELECT *
                         FROM sp spy
                        WHERE spy.sno = spx.sno
                              AND spy.pno = 'P1'
                              AND spy.qty = 1000
                      );

So NOT EXISTS nicht der Königsweg ist, kann es erschienen ist!

Natürlich Quelle des Problems ist das Vorhandensein von NULL-Werten, also die ‚echte‘ Lösung ist es, die NULL-Werte zu eliminieren.

Dies kann erreicht werden (unter anderen möglichen Design) mit zwei Tabellen:

  • sp Lieferanten bekannten Teile liefern
  • spq Lieferanten bekannte Teile in bekannten Mengen liefern

Hinweis darauf, es sollte wohl ein Fremdschlüssel sein, wo spq Referenzen sp.

Das Ergebnis kann dann erhalten werden, unter Verwendung des ‚minus‘ Vergleichsoperator (wobei das EXCEPT Schlüsselwort in Standard-SQL) z.B.

WITH sp AS 
     ( SELECT * 
         FROM ( VALUES ( 'S1', 'P1' ), 
                       ( 'S2', 'P1' ),
                       ( 'S3', 'P1' ) )
              AS T ( sno, pno )
     ),
     spq AS 
     ( SELECT * 
         FROM ( VALUES ( 'S2', 'P1', 200 ),
                       ( 'S3', 'P1', 1000 ) )
              AS T ( sno, pno, qty )
     )
SELECT sno
  FROM spq
 WHERE pno = 'P1'
EXCEPT 
SELECT sno
  FROM spq
 WHERE pno = 'P1'
       AND qty = 1000;

Wenn Sie mit NOT IN für eine Unterabfrage filtern möchten enthält NULL-Werte nur überprüfen nicht null

SELECT blah FROM t WHERE blah NOT IN
        (SELECT someotherBlah FROM t2 WHERE someotherBlah IS NOT NULL )

Dies ist für Jungen:

select party_code 
from abc as a
where party_code not in (select party_code 
                         from xyz 
                         where party_code = a.party_code);

Dies funktioniert unabhängig von ansi-Einstellungen

Auch dies könnte von Nutzen sein, den logischen Unterschied zu kennen zwischen verbinden, existiert und in http://weblogs.sqlteam.com/mladenp/archive/ 2007/05/18 / 60210.aspx

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top