Wie erstelle ich einen eindeutigen Index für eine NULL-Spalte?

101

Ich verwende SQL Server 2005. Ich möchte die Werte in einer Spalte so einschränken, dass sie eindeutig sind, während NULLS zulässig ist.

Meine aktuelle Lösung beinhaltet einen eindeutigen Index für eine Ansicht wie folgt:

CREATE VIEW vw_unq WITH SCHEMABINDING AS
    SELECT Column1
      FROM MyTable
     WHERE Column1 IS NOT NULL

CREATE UNIQUE CLUSTERED INDEX unq_idx ON vw_unq (Column1)

Irgendwelche besseren Ideen?

Nuno G.
quelle
16
Keine Chance, SQL 2008 zu nutzen? Sie können einen gefilterten Index mit 'where'
erstellen
3
Sie haben nicht eindeutig gemeint und NULL-Werte zugelassen . Sie haben anscheinend eindeutig gemeint , aber mehrere NULL-Werte eingeschlossen . Andernfalls wird NULL wie jeder andere Wert indiziert und die Eindeutigkeitsbeschränkung funktioniert wie erwartet - nur nicht gemäß den SQL-Standards, wie @pst in einem Kommentar unten erwähnt.
Suncat2000

Antworten:

26

Ich bin mir ziemlich sicher, dass Sie das nicht tun können, da dies den Zweck von Unikaten verletzt.

Diese Person scheint jedoch eine anständige Arbeit zu haben: http://sqlservercodebook.blogspot.com/2008/04/multiple-null-values-in-unique-index-in.html

willasaywhat
quelle
2
Es scheint, dass der Inhalt des von Ihnen bereitgestellten Links tatsächlich (teilweise) ohne Namensnennung von hier kopiert wurde: decipherinfosys.wordpress.com/2007/11/30/…
Tom Juergens
77
Ich bin nicht der Meinung, dass es "den Zweck von Unikaten verletzt" - NULL ist ein spezieller Wert in SQL (in vielerlei Hinsicht ähnlich wie NaN) und muss entsprechend behandelt werden. In SQL Server ist es tatsächlich ein Fehler, verschiedene SQL-Spezifikationen zu berücksichtigen : Hier ist ein Link für eine Anfrage nach der "korrekten Implementierung" für das, was es wert ist: connect.microsoft.com/SQLServer/feedback/details/299229/… .
5
Als Referenz für 2008 können Sie CREATE UNIQUE INDEX foo ON dbo.bar (Schlüssel) ausführen, wobei der Schlüssel NICHT NULL ist.
Niico
2
Ich bin auch nicht einverstanden mit "verletzt den Zweck von Unikaten", NULL ist nicht gleich NULL, daher sollten Sie in der Lage sein, einen eindeutigen Index für eine nullfähige Spalte zu erstellen und mehrere Nullen einzufügen.
Wodzu
105

Mit SQL Server 2008 können Sie einen gefilterten Index erstellen: http://msdn.microsoft.com/en-us/library/cc280372.aspx . (Ich sehe, dass Simon dies als Kommentar hinzugefügt hat, dachte aber, dass es eine eigene Antwort verdient, da der Kommentar leicht übersehen wird.)

Eine weitere Option ist ein Auslöser zum Überprüfen der Eindeutigkeit. Dies kann jedoch die Leistung beeinträchtigen.

Phil Haselden
quelle
84
create unique index UIX on MyTable (Column1) where Column1 is not null
Jørn Schou-Rode
1
Hinweis: Derzeit scheint SQL Server Management Studio nicht zu wissen, wie solche Indizes erstellt werden. Wenn Sie die Tabelle später ändern, wird sie verwirrt und versucht, sie zu
löschen. Denken
3
Es scheint, dass Microsoft SSMS aktualisiert hat, um dies zu unterstützen. Ich habe SSMS 10.50.1617 und im Dialogfeld Indexeigenschaften können Sie die Seite Filter auswählen, um den Filter zu bearbeiten. zB "([Spalte1] IST NICHT NULL)"
Phil Haselden
5
Das Zulassen mehrerer Nullen in einem Index und das Filtern von Nullen aus einem Index sind separate Dinge. Durch das Filtern eines Index werden Datensätze tatsächlich aus dem Index ausgeschlossen, während die anderen Lösungen die Null in einen nützlichen eindeutigen Wert umwandeln. Sei dir des Unterschieds bewusst.
Suncat2000
Wenn Sie gespeicherte Prozeduren für eine Tabelle mit einem solchen gefilterten Index verwenden, stellen Sie sicher, dass dies der Fall ANSI_NULLSist ON. Andernfalls wird beim Einfügen von Daten eine Fehlermeldung angezeigt.
Arne
71

Der berechnete Spaltentrick ist allgemein als "Nullbuster" bekannt. meine Notizen Kredit Steve Kass:

CREATE TABLE dupNulls (
pk int identity(1,1) primary key,
X  int NULL,
nullbuster as (case when X is null then pk else 0 end),
CONSTRAINT dupNulls_uqX UNIQUE (X,nullbuster)
)
eines Tages, wenn
quelle
Das sieht nach einem coolen Trick aus. Seltsamerweise bringt die Suche nach Nullbuster nicht zu viel Zeug hervor. Ich frage mich, ob dies auch nützlich sein wird, um die Suche zu beschleunigen - anstatt einer berechneten Spalte von nur 1 und 0 für null oder nicht, wenn die Verwendung der PK dem Index etwas mehr bietet, mit dem er arbeiten kann. Ich werde dieses Wochenende an einem großen Tisch testen und sehen.
David Storfer
@DavidStorfer, das können Sie nicht, weil Sie eine Kollision zwischen den IDs der beiden verschiedenen Tabellen haben könnten.
user393274
Verbesserung: ISNULL (X, CONVERT (VARCHAR (10), pk))
Faiz
5
@Faiz: Verbesserung liegt im Auge des Betrachters. Ich bevorzuge das Aussehen des Originals.
Tag, wenn der
@NunoG, dies sollte die akzeptierte Antwort sein, da es eine gute Lösung bietet, die Ihren Anforderungen entspricht, anstatt nur eine externe Site zu verlinken, die möglicherweise verschwindet.
Frédéric
-3

Genau genommen kann eine eindeutige nullfähige Spalte (oder ein Satz von Spalten) nur einmal NULL (oder ein Datensatz von NULL) sein, da der gleiche Wert (und dies schließt NULL ein) mehr als einmal offensichtlich gegen die eindeutige Einschränkung verstößt.

Dies bedeutet jedoch nicht, dass das Konzept der "eindeutigen nullbaren Spalten" gültig ist. Um es tatsächlich in einer relationalen Datenbank zu implementieren, müssen wir nur berücksichtigen, dass diese Art von Datenbanken normalisiert werden soll, um ordnungsgemäß zu funktionieren. Bei der Normalisierung werden normalerweise mehrere (Nicht-Entitäts-) zusätzliche Tabellen hinzugefügt, um Beziehungen zwischen den Entitäten herzustellen .

Lassen Sie uns ein einfaches Beispiel für nur eine "eindeutige nullfähige Spalte" erstellen. Es ist einfach, sie auf weitere solche Spalten zu erweitern.

Angenommen, wir haben die Informationen, die durch eine Tabelle wie diese dargestellt werden:

create table the_entity_incorrect
(
  id integer,
  uniqnull integer null, /* we want this to be "unique and nullable" */
  primary key (id)
);

Wir können dies tun, indem wir uniqnull auseinander setzen und eine zweite Tabelle hinzufügen, um eine Beziehung zwischen uniqnull-Werten und the_entity herzustellen (anstatt uniqnull "innerhalb" der_entity zu haben):

create table the_entity
(
  id integer,
  primary key(id)
);

create table the_relation
(
  the_entity_id integer not null,
  uniqnull integer not null,

  unique(the_entity_id),
  unique(uniqnull),
  /* primary key can be both or either of the_entity_id or uniqnull */
  primary key (the_entity_id, uniqnull), 
  foreign key (the_entity_id) references the_entity(id)
);

Um einer Zeile in the_entity den Wert uniqnull zuzuordnen, müssen wir auch eine Zeile in the_relation hinzufügen.

Für Zeilen in the_entity, in denen keine eindeutigen Werte zugeordnet sind (dh für diejenigen, die NULL in the_entity_incorrect setzen würden), fügen wir einfach keine Zeile in the_relation hinzu.

Beachten Sie, dass die Werte für uniqnull für die gesamte Beziehung eindeutig sind, und beachten Sie auch, dass es für jeden Wert in der Entität höchstens einen Wert in der Beziehung geben kann, da der Primär- und der Fremdschlüssel dies erzwingen.

Wenn dann ein Wert von 5 für uniqnull mit einer the_entity-ID von 3 verknüpft werden soll, müssen wir:

start transaction;
insert into the_entity (id) values (3); 
insert into the_relation (the_entity_id, uniqnull) values (3, 5);
commit;

Und wenn ein ID-Wert von 10 für the_entity kein eindeutiges Gegenstück hat, tun wir nur:

start transaction;
insert into the_entity (id) values (10); 
commit;

Um diese Informationen zu denormalisieren und die Daten zu erhalten, die eine Tabelle wie the_entity_incorrect enthalten würde, müssen wir:

select
  id, uniqnull
from
  the_entity left outer join the_relation
on
  the_entity.id = the_relation.the_entity_id
;

Der Operator "linker äußerer Join" stellt sicher, dass alle Zeilen aus der Entität im Ergebnis angezeigt werden, und setzt NULL in die eindeutige Spalte, wenn in der Beziehung keine übereinstimmenden Spalten vorhanden sind.

Denken Sie daran, dass jeder Aufwand, der für einige Tage (oder Wochen oder Monate) beim Entwerfen einer gut normalisierten Datenbank (und der entsprechenden denormalisierenden Ansichten und Verfahren) aufgewendet wird, Ihnen Jahre (oder Jahrzehnte) an Schmerz und verschwendeten Ressourcen erspart.

Roy
quelle
6
Wie bereits im Kommentar der akzeptierten Antwort mit fünfzig Upvotes angegeben, sollte es von MS SQL Server unterstützt werden, mehrere Nullen in einer als eindeutig indizierten Spalte zu haben. Es ist ein Fehler, SQL-Standards zu implementieren, um dies nicht zuzulassen. Null ist kein Wert, null ist nicht gleich null, das ist eine grundlegende SQL-Regel seit Jahren. Ihr erster Satz ist also falsch und die meisten Leser werden sich nicht die Mühe machen, weiterzulesen.
Frédéric