Warum erlaubt eine UNIQUE-Einschränkung nur eine NULL?


36

Technisch gesehen ist NULL = NULL Falsch. Nach dieser Logik ist kein NULL-Wert gleich einem NULL-Wert und alle NULL-Werte sind unterschiedlich. Sollte dies nicht bedeuten, dass alle NULL-Werte eindeutig sind und ein eindeutiger Index eine beliebige Anzahl von NULL-Werten zulassen sollte?


Kommentare sind nicht für eine längere Diskussion gedacht. Diese Unterhaltung wurde in den Chat verschoben .
Paul White sagt GoFundMonica

Antworten:


52

Warum funktioniert das so? Denn vor langer Zeit traf jemand eine Entwurfsentscheidung, ohne zu wissen oder sich darum zu kümmern, was der Standard sagt (schließlich haben wir alle möglichen seltsamen Verhaltensweisen mit NULLs und können nach Belieben ein anderes Verhalten erzwingen). Diese Entscheidung diktierte, dass in diesem Fall NULL = NULL.

Es war keine sehr kluge Entscheidung. Was sie getan haben sollten, ist, dass das Standardverhalten dem ANSI-Standard entspricht, und wenn sie dieses besondere Verhalten wirklich wollten, lassen Sie es durch eine DDL-Option wie WITH CONSIDER_NULLS_EQUALoder zu WITH ALLOW_ONLY_ONE_NULL.

Natürlich ist im Nachhinein 20/20.

Und wir haben jetzt sowieso einen Workaround, auch wenn es nicht der sauberste oder intuitivste ist.

Sie können das richtige ANSI-Verhalten in SQL Server 2008 und höher erzielen, indem Sie einen eindeutigen, gefilterten Index erstellen.

CREATE UNIQUE INDEX foo ON dbo.bar(key) WHERE key IS NOT NULL;

Dies ermöglicht mehr als einen NULLWert, da diese Zeilen bei der Duplikatprüfung nicht berücksichtigt werden. Als zusätzlichen Bonus wäre dies ein kleinerer Index als ein Index, der aus der gesamten Tabelle besteht, wenn mehrere NULLs zulässig wären (insbesondere, wenn es nicht die einzige Spalte im Index ist, INCLUDESpalten usw. enthält). Möglicherweise möchten Sie jedoch einige der anderen Einschränkungen von gefilterten Indizes kennen:


8

Richtig. Die Implementierung einer eindeutigen Einschränkung oder eines eindeutigen Indexes in SQL Server ermöglicht nur einen NULL-Wert. Korrigieren Sie auch, dass dies technisch nicht mit der Definition von NULL übereinstimmt, aber es ist eines der Dinge, die sie getan haben, um es nützlicher zu machen, obwohl es nicht "technisch" korrekt ist. Beachten Sie, dass ein PRIMARY KEY (auch ein eindeutiger Index) (natürlich) keine NULL-Werte zulässt.


1
Diese (SQL-Server-) Technik passt auch nicht zum SQL-Standard. Es gibt ein 7 Jahre altes Connect-Element zu diesem Problem.
Ypercubeᵀᴹ

@ypercube True. Deshalb habe ich gesagt, dass es nur die Implementierung ist und nicht wirklich zur Definition von NULL passt. Ich hatte nicht über den gefilterten eindeutigen Index nachgedacht (obwohl ich ihn für andere Dinge verwendet habe.)
Kenneth Fisher

3

Hören Sie auf, den Ausdruck "Nullwert" zu verwenden, da Sie sonst in die Irre geführt werden. Verwenden Sie stattdessen den Ausdruck "Null-Markierung" - eine Markierung in einer Spalte, die angibt, dass der tatsächliche Wert in dieser Spalte fehlt oder nicht anwendbar ist (beachten Sie jedoch, dass die Markierung nicht angibt, welche dieser Optionen tatsächlich der Fall ist¹).

Stellen Sie sich nun Folgendes vor (wobei die Datenbank die modellierte Situation nicht vollständig kennt).

Situation          Database

ID   Code          ID   Code
--   -----         --   -----
1    A             1    A
2    B             2    (null)
3    C             3    C
4    B             4    (null)

Die Integritätsregel, die wir modellieren, lautet "Der Code muss eindeutig sein". Die reale Situation verstößt dagegen, sodass die Datenbank nicht zulässt, dass sich die Elemente 2 und 4 gleichzeitig in der Tabelle befinden.

Der sicherste und am wenigsten flexible Ansatz besteht darin, Nullmarkierungen im Feld Code nicht zuzulassen, sodass keine inkonsistenten Daten möglich sind. Der flexibelste Ansatz besteht darin, mehrere Nullmarken zuzulassen und sich bei der Eingabe von Werten um die Eindeutigkeit zu sorgen.

Die Sybase-Programmierer entschieden sich für den etwas sicheren, nicht sehr flexiblen Ansatz, nur einen Null-Marker in der Tabelle zuzulassen - worüber sich Kommentatoren seitdem beschwert haben. Microsoft hat dieses Verhalten fortgesetzt, ich denke für die Abwärtskompatibilität.


¹ Ich bin mir sicher, dass ich irgendwo gelesen habe, dass Codd überlegt hat, zwei Null-Marker zu implementieren - einen für unbekannt, einen für nicht zutreffend -, diesen aber abgelehnt habe, aber ich kann den Verweis nicht finden. Erinnere ich mich richtig?

PS Mein Lieblingszitat zu null: Louis Davidson, "Professionelles SQL Server 2000-Datenbankdesign", Wrox Press, 2001, Seite 52. "Auf einen einzigen Satz gebracht: NULL ist böse."


1
Das Erlauben einer einzelnen Person nullerreicht dieses Ziel ebenfalls nicht. Weil sich herausstellen kann, dass der fehlende Wert mit dem Wert in einer der anderen Zeilen übereinstimmt.
Martin Smith

1
Was @MartinSmith gesagt hat. Was ist, wenn Sie eine Check-Einschränkung haben CHECK (Value IN ('A','B','C','D'))? Dann erlauben sowohl die Implementierung von SQL-Server als auch der SQL-Standard, dass die Tabelle 5 Zeilen enthält (eine Zeile für jeden Wert plus 1 mit NULL). Während die Datenbank mit ihren Einschränkungen konsistent ist, stimmt sie wohl nicht mit der Absicht des Designers überein Die Tabelle darf maximal 4 Zeilen enthalten. Es gibt keinen Wert, in den NULL geändert werden kann, der keine Einschränkung verletzt, es sei denn, eine oder mehrere Zeilen werden gelöscht.
Ypercubeᵀᴹ

1
Die Tatsache, dass der Standard 6 sogar 106 Zeilen anstelle von 5 zulässt, ändert nichts daran, dass in diesem Szenario beide fehlschlagen.
ypercubeᵀᴹ

@Martin Smith, es könnte sein, aber es könnte auch nicht sein - der Datenbankserver kann es nicht sagen, also riskiert er es nicht und geht den sicheren Weg. Das haben die Sybase-Programmierer (nehme ich an) entschieden, was seitdem für Ärger gesorgt hat (zumindest in Inside SQL Server 6.5, dem ältesten Buch in meinem Bücherregal, in dem Ron Soukup in etwa den gleichen Kommentar macht, den Aaron Bertrand in seiner Antwort macht). . Ich denke, es könnte schlimmer sein - sie hätten keine Null-Marker vorschreiben können. :-)
Greenstone Walker

2
@ GreenstoneWalker - Es geht nicht auf dem "sicheren" Weg. Es wird davon ausgegangen, dass der fehlende Wert nicht in Konflikt gerät. CREATE TABLE #T(A INT NULL UNIQUE);INSERT INTO #T VALUES (1),(NULL);UPDATE #T SET A = 1 WHERE A IS NULL;wird einen Fehler auslösen. Nach Ihrer Theorie der Gestaltungsmotive hätte das Einfügen NULLim ersten Fall verhindert werden müssen - denn das unvollständige Wissen bedeutet, dass es keine Garantie dafür gibt, dass der Wert unterschiedlich ist.
Martin Smith

2

Das mag technisch nicht korrekt sein, aber philosophisch hilft es mir, nachts zu schlafen ...

Wie mehrere andere gesagt oder angedeutet haben, können Sie, wenn Sie NULL als unbekannt betrachten, nicht feststellen, ob ein NULL-Wert tatsächlich einem anderen NULL-Wert entspricht. Auf diese Weise sollte der Ausdruck NULL == NULL zu NULL ausgewertet werden, was "unbekannt" bedeutet.

Eine eindeutige Einschränkung würde einen endgültigen Wert für den Vergleich der Spaltenwerte benötigen. Mit anderen Worten, wenn ein einzelner Spaltenwert mit einem anderen Spaltenwert unter Verwendung des Gleichheitsoperators verglichen wird, muss er mit false bewertet werden, um gültig zu sein. Unbekannt ist nicht wirklich falsch, obwohl es oft als falsch behandelt wird. Zwei NULL-Werte können gleich sein oder nicht ... sie können einfach nicht definitiv bestimmt werden.

Es ist hilfreich, sich eine eindeutige Einschränkung als einschränkende Werte vorzustellen, bei denen festgestellt werden kann, dass sie sich voneinander unterscheiden. Was ich damit meine ist, wenn Sie ein SELECT ausführen, das ungefähr so ​​aussieht:

SELECT * from dbo.table1 WHERE ColumnWithUniqueContraint="some value"

Die meisten Menschen würden ein Ergebnis erwarten, da es eine einzigartige Einschränkung gibt. Wenn Sie in ColumnWithUniqueConstraint mehrere NULL-Werte zulassen, ist es unmöglich, eine einzelne Zeile aus der Tabelle auszuwählen, wobei NULL als Vergleichswert verwendet wird.

Angesichts dessen glaube ich, dass es in den meisten Situationen viel praktischer ist, als mehrere NULL-Werte zuzulassen, unabhängig davon, ob es in Bezug auf die Definition von NULL korrekt implementiert ist oder nicht.


Ihr Select liefert 1 Ergebnis, wenn es eine Unique-Einschränkung gibt (in jeder Implementierung, nicht nur in SQL-Server). Worum geht es dir?
Ypercubeᵀᴹ

-3

Einer der Hauptzwecke einer UNIQUEEinschränkung besteht darin, doppelte Datensätze zu verhindern. Wenn eine Tabelle benötigt wird, in der es mehrere Datensätze geben kann, in denen ein Wert "unbekannt" ist, aber keine zwei Datensätze denselben "bekannten" Wert haben dürfen, sollten den unbekannten Werten künstliche eindeutige Bezeichner zugewiesen werden, bevor sie vorhanden sind zur Tabelle hinzugefügt.

Es gibt einige seltene Fälle, in denen eine Spalte eine UNIQUEEinschränkung aufweist und einen einzelnen Nullwert enthält. Wenn eine Tabelle beispielsweise eine Zuordnung zwischen Spaltenwerten und lokalisierten Textbeschreibungen enthält, kann in einer Zeile für NULLdie Beschreibung definiert werden, die angezeigt werden soll, wenn sich diese Spalte in einer anderen Tabelle befindet NULL. Das Verhalten von NULLlässt diesen Anwendungsfall zu.

Ansonsten sehe ich keine Grundlage für eine Datenbank mit einer UNIQUEEinschränkung für eine Spalte, die das Vorhandensein vieler identischer Datensätze zulässt, aber ich sehe keine Möglichkeit, dies zu verhindern, während mehrere Datensätze zugelassen werden, deren Schlüsselwerte nicht unterscheidbar sind. Wenn Sie angeben, dass dies NULLnicht mit sich selbst NULLübereinstimmt, können die Werte nicht voneinander unterschieden werden.


3
Künstliche eindeutige Kennungen sind ein Witz, sorry. Wie machst du das für eine FIN? Wenn Sie nicht wissen, was es ist, warum etwas erfinden? Nur um zusätzlichen Speicherplatz zu belegen? Es scheint unsinnig, ein anderes Problem zu umgehen (zum Beispiel die Anwendung nicht so schreiben zu wollen, dass sie ordnungsgemäß mit NULL umgeht). Wenn Sie unbedingt wissen müssen, warum etwas NULL ist (vorhanden, aber unbekannt oder nicht vorhanden oder nicht bekannt oder egal, ob es beispielsweise vorhanden ist), fügen Sie eine Art Statusspalte hinzu. Tokens führen nur zu umständlichem Code, um mit ihnen umzugehen.
Aaron Bertrand

Viel hängt vom Zweck der Eindeutigkeitsbeschränkung ab. Wenn ein Feld als Bezeichner verwendet wird, sollte es nicht null sein. In Fällen (wie bei VINs), in denen Geschäftsregeln darauf hindeuten, dass bei zweimaligem Auftauchen eines Elements eines falsch sein muss, einige Elemente jedoch möglicherweise "Weiß nicht" lauten, scheint eine Eindeutigkeitsbeschränkung nicht der richtige Ansatz zu sein. Wenn ein Fahrzeug mit einer bekannten VIN in Konflikt mit einer anderen in der Datenbank steht, weiß man möglicherweise, dass mindestens eine der VIN falsch ist. Es ist jedoch besser, wenn die Datenbank den geschätzten Wert für beide Datensätze meldet, als zu raten das ist richtig.
Supercat

@AaronBertrand: Es gibt einige Fälle, in denen ein möglicherweise-null eindeutiges-wenn-nicht-null Feld ein Ersatzschlüssel sein müsste, der nicht vor dem Ausfüllen des Feldes festgelegt werden konnte (z. B. "Ehepartner-ID"), aber in Situationen wie dass eine "eindeutige" Einschränkung unzureichend wäre; Wenn X.Spouse nicht null ist, muss X.Spouse.Spouse = X sein. Im Übrigen könnte so etwas wie "Ehepartner" auch so gehandhabt werden, dass der Datensatz für eine unverheiratete Person nicht "NULL" als Ehepartner haben sollte, sondern eine eigene ID. In diesem Fall könnte die X.spouse.spouse = X-Regel verwendet werden gelten für alle.
Supercat
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.