9

Ich habe das schon ein paar Mal in meiner Karriere kommen, und keiner meiner lokalen Kollegen scheint in der Lage zu sein, es zu beantworten. Angenommen, ich habe eine Tabelle mit einem "Description" -Feld, das ein Kandidatentaste ist, mit der Ausnahme, dass ein Benutzer manchmal die Hälfte des Prozesses stoppt. Für etwa 25% der Datensätze ist dieser Wert null, aber für alle, die nicht NULL sind, muss er eindeutig sein.SQL kann ich eine bedingte Eindeutigkeit für eine Tabelle haben?

Ein anderes Beispiel könnte eine Tabelle sein, die mehrere "Versionen" eines Datensatzes verwalten muss, und ein Bitwert zeigt an, welcher der "aktiven" ist. Der "Kandidatenschlüssel" ist also immer gefüllt, aber es gibt möglicherweise drei Versionen, die identisch sind (mit 0 im aktiven Bit) und nur eine, die aktiv ist (1 im aktiven Bit).

Ich habe alternative Methoden, um diese Probleme zu lösen (im ersten Fall erzwingen Sie den Regelcode, entweder in der gespeicherten Prozedur oder Business-Schicht, und in der zweiten füllen eine Archivtabelle mit einem Trigger und UNION die Tabellen, wenn ich brauche eine Geschichte). Ich möchte keine Alternativen (es sei denn, es gibt nachweislich bessere Lösungen), ich frage mich nur, ob irgendein Geschmack von SQL auf diese Weise "bedingte Eindeutigkeit" ausdrücken kann. Ich benutze MS SQL, wenn es einen Weg gibt, das zu tun, großartig. Ich bin hauptsächlich akademisch an dem Problem interessiert.

+2

Mögliche doppelte: http://stackoverflow.com/questions/866061/conditional-unique-constraint –

+1

Welche Version von SQL Server? – gbn

+0

Danke für den Zeiger In silico. Es trifft definitiv auf Situation 2 zu. Ich habe gesucht, bevor ich gepostet habe, aber ich habe nicht wirklich nach den Schlüsselwörtern gesucht, die ich schließlich im Titel verwendet habe, oder ich hätte es gefunden! Der Link, den du postest, geht nicht ganz auf Situation Nr. 1 ein, die mit Tom H.'s Vorschlag von 2005 und Arthur in 2008 gelöst wurde. gbn: Ich benutze 2005, weshalb ich nichts über Index wusste Filter in 2008. –

Antwort

28

Wenn Sie SQL Server 2008 ein Index-Filter verwenden würde vielleicht Ihre Lösung:

http://msdn.microsoft.com/en-us/library/ms188783.aspx

Dies ist, wie ich einen eindeutigen Index mit mehreren NULL

CREATE UNIQUE INDEX [IDX_Blah] ON [tblBlah] ([MyCol]) WHERE [MyCol] IS NOT NULL 
+1

Ah - funktionsbasierter Index ... DBAs verabscheuen diese –

+0

@OMG Ponys während sucky für DBAs es ist über die einzige realisierbare Lösung – msarchet

+0

@msarchet: Arbeit ist im Weg, ATM zu helfen, aber die Tabelle, die im OP beschrieben wird, ist nicht ideal für mich. Aber genieße es, mit DBAs zu kämpfen =) –

0

Danke für die Kommentare, die erste Version dieser Antwort war falsch.

Hier ist ein Trick, um eine berechnete Spalte, die effektiv eine Nullable-eindeutige Einschränkung in SQL Server ermöglicht:

create table NullAndUnique 
    (
    id int identity, 
    name varchar(50), 
    uniqueName as case 
     when name is null then cast(id as varchar(51)) 
     else name + '_' end, 
    unique(uniqueName) 
    ) 

insert into NullAndUnique default values 
insert into NullAndUnique default values -- Works 
insert into NullAndUnique default values -- not accidentally :) 
insert into NullAndUnique (name) values ('Joel') 
insert into NullAndUnique (name) values ('Joel') -- Boom! 

Es nutzt grundsätzlich die id wenn die name null ist. Die + '_' soll Fälle vermeiden, in denen der Name numerisch sein könnte, wie 1, die mit der id kollidieren könnten.

+1

Wirklich? Von MSDN: "Spalten, die in einem eindeutigen Index verwendet werden, sollten auf NOT NULL festgelegt werden, da mehrere NULL-Werte als Duplikate gelten, wenn ein eindeutiger Index erstellt wird." – Arthur

+6

Nein, SQL Server lässt * NOT * mehrere Nullwerte in einer Spalte zu, die von einer eindeutigen Integritätsbedingung abgedeckt wird. Der SQL-Standard gibt an, dass dies zulässig sein soll (da NULL nicht gleich selbst ist), aber Microsoft SQL Server hat den Standard in diesem Punkt nicht eingehalten. –

0

Die andere Methode Werte erzwingen zum Erzwingen von bedingten Regeln, die ein eindeutiger Index oder eine eindeutige Prüfbedingung nicht verarbeiten kann, ist die Verwendung eines Triggers.

1

Oracle tut. Ein vollständiger Nullschlüssel wird in Oracle nicht durch eine B Struktur im Index indiziert, und Oracle verwendet B Baumindizes, um eindeutige Integritätsbedingungen zu erzwingen.

ein Unter der Annahme, wollte Version id_spalte auf der Grundlage des ACTIVE_FLAG auf 1 gesetzt werden:

CREATE UNIQUE INDEX idx_versioning_id ON mytable 
    (CASE active_flag WHEN 0 THEN NULL ELSE active_flag END, 
    CASE active_flag WHEN 0 THEN NULL ELSE id_column END); 
2

Im Fall von Beschreibungen, die noch nicht abgeschlossen sind, würde ich nicht die in der gleichen Tabelle hat wie die finalisiert Beschreibungen. Die finale Tabelle würde dann einen eindeutigen Index oder Primärschlüssel in der Beschreibung haben.

Im Falle der aktiven/inaktiv, wieder habe ich möglicherweise separate Tabellen wie Sie mit einem "Archiv" oder "Geschichte" -Tabelle, aber eine andere Möglichkeit, es zumindest in MS SQL Server zu tun ist durch die Verwendung einer indizierten Sicht:

CREATE TABLE Test_Conditionally_Unique 
(
    my_id INT NOT NULL, 
    active BIT NOT NULL DEFAULT 0 
) 
GO 
CREATE VIEW dbo.Test_Conditionally_Unique_View 
WITH SCHEMABINDING 
AS 
    SELECT 
     my_id 
    FROM 
     dbo.Test_Conditionally_Unique 
    WHERE 
     active = 1 
GO 
CREATE UNIQUE CLUSTERED INDEX IDX1 ON Test_Conditionally_Unique_View (my_id) 
GO 

INSERT INTO dbo.Test_Conditionally_Unique (my_id, active) 
VALUES (1, 0) 
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active) 
VALUES (1, 0) 
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active) 
VALUES (1, 0) 
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active) 
VALUES (1, 1) 
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active) 
VALUES (2, 0) 
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active) 
VALUES (2, 1) 
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active) 
VALUES (2, 1) -- This insert will fail 

Sie könnten diese Methode auch für die NULL/Wertebeschreibungen verwenden.

+0

Danke, das funktioniert im Jahr 2005, und ich habe gerade heute mit indizierten Sichten auf ein anderes Problem herumgespielt. Das ist eine wirklich großartige Anwendung davon! –

0

Ich bin mir Ihrer beabsichtigten Verwendung oder Ihrer Tabellen nicht ganz bewusst, aber Sie könnten versuchen, eine Eins-zu-eins-Beziehung zu verwenden. Teilen Sie diese "manchmal" eindeutige Spalte in eine neue Tabelle auf, erstellen Sie den UNIQUE-Index für diese Spalte in der neuen Tabelle und FK zurück zur ursprünglichen Tabelle, indem Sie die ursprünglichen Tabellen PK verwenden. Haben Sie nur eine Zeile in dieser neuen Tabelle, wenn die "eindeutigen" Daten existieren sollen.

ALTE Tabellen:

TableA 
ID pk 
Col1 sometimes unique 
Col... 

NEW Tabellen:

TableA 
ID 
Col... 

TableB 
ID PK, FK to TableA.ID 
Col1 unique index 
Verwandte Themen