Frage

Hier ist ein kleines Experiment, das ich in einer Oracle-Datenbank lief (10 g). Abgesehen von (Oracle) Implementierung Bequemlichkeit, ich kann nicht herausfinden, warum einige Einfügungen akzeptiert und andere abgelehnt.

create table sandbox(a number(10,0), b number(10,0));
create unique index sandbox_idx on sandbox(a,b);

insert into sandbox values (1,1); -- accepted
insert into sandbox values (1,2); -- accepted
insert into sandbox values (1,1); -- rejected

insert into sandbox values (1,null); -- accepted
insert into sandbox values (2,null); -- accepted
insert into sandbox values (1,null); -- rejected

insert into sandbox values (null,1); -- accepted
insert into sandbox values (null,2); -- accepted
insert into sandbox values (null,1); -- rejected

insert into sandbox values (null,null); -- accepted
insert into sandbox values (null,null); -- accepted

Unter der Annahme, dass es Sinn macht, gelegentlich einige Zeilen mit einigen Spaltenwerte unbekannt, kann ich denke an zwei mögliche Anwendungsfälle beteiligt zu verhindern Duplikate:
 1. Ich möchte Duplikate ablehnen, aber akzeptieren, wenn irgendeines eingeschränkten Spalte Wert unbekannt ist.
 2. Ich möchte Duplikate verwerfen, auch in Fällen, in denen eine eingeschränkte Spalte Wert unbekannt ist.

Offenbar implementiert Oracle etwas anderes aber:
 3. Ablehnen Duplikate, aber akzeptieren (nur), wenn alle gezwungen Spaltenwerte nicht bekannt sind.

kann ich von Möglichkeiten denkt Verwendung von Oracle-Implementierung zu machen zu bekommen Fall zu verwenden (2) - zum Beispiel einen besonderen Wert für „unbekannt“ haben, und die Spalten nicht-nullable zu machen. Aber ich kann nicht herausfinden, wie man Fall verwenden (1).

Mit anderen Worten, wie kann ich Oracle so zu handeln bekommen?

create table sandbox(a number(10,0), b number(10,0));
create unique index sandbox_idx on sandbox(a,b);

insert into sandbox values (1,1); -- accepted
insert into sandbox values (1,2); -- accepted
insert into sandbox values (1,1); -- rejected

insert into sandbox values (1,null); -- accepted
insert into sandbox values (2,null); -- accepted
insert into sandbox values (1,null); -- accepted

insert into sandbox values (null,1); -- accepted
insert into sandbox values (null,2); -- accepted
insert into sandbox values (null,1); -- accepted

insert into sandbox values (null,null); -- accepted
insert into sandbox values (null,null); -- accepted
War es hilfreich?

Lösung 2

create unique index sandbox_idx on sandbox
 (case when a is null or b is null then null else a end,
  case when a is null or b is null then null else b end);

Ein Funktions Index! Grundsätzlich Ich brauchte nur um sicherzustellen, dass alle Tupel ich ignorieren möchten (dh - akzeptieren) an alle NULL-Werte übersetzen lassen. Hässlich, aber nicht hässlich Hintern. Funktioniert wie gewünscht.

es mit Hilfe einer Lösung auf eine andere Frage Figured aus: Wie eine Datenbanktabelle zu beschränken, so dass nur eine Zeile einen bestimmten Wert in einer Spalte haben kann?

So gehen dort und geben Tony Andrews auch Punkte. :)

Andere Tipps

Versuchen Sie, eine funktionsbasierte Index:

Erstellen eindeutigen Index sandbox_idx auf Sandbox (Fall, wenn ein NULL NULL IST DANN, WENN b NULL ist, werden NULL ELSE ein || '' || b END);

Es gibt auch andere Möglichkeiten, um diese Katze zu Haut, aber dies ist einer von ihnen.

Ich bin kein Oracle Kerl, aber hier ist eine Idee, die funktionieren soll, wenn Sie eine berechnete Spalte in einem Index in Oracle enthalten können.

Fügen Sie eine zusätzliche Spalte auf den Tisch (und Ihren UNIQUE-Index), die wie folgt berechnet wird: es NULL ist, wenn a und b nicht-NULL ist, und es ist die Primärschlüssel der Tabelle anders. Ich nenne diese zusätzliche Spalte „nullbuster“ aus offensichtlichen Gründen.

alter table sandbox add nullbuster as 
  case when a is null or b is null then pk else null end;
create unique index sandbox_idx on sandbox(a,b,pk);

Ich habe dieses Beispiel einige Male um 2002 oder so in der Usenet-Gruppe microsoft.public.de.sqlserver. Sie können die Diskussionen finden, wenn Sie groups.google.com für das Wort „nullbuster“ suchen. Die Tatsache, dass Sie Oracle verwenden sollte nicht viel aus.

.

P. S In SQL Server wird diese Lösung ziemlich durch gefilterte Indizes ersetzt:

create unique index sandbox_idx on sandbox(a,b)
(where a is not null and b is not null);

Der Thread Sie verwiesen darauf hin, dass Oracle Sie diese Option nicht geben. Ist es nicht auch die Möglichkeit einer indizierten Sicht haben, die eine weitere Alternative ist?

create view sandbox_for_unique as
select a, b from sandbox
where a is not null and b is not null;

create index sandbox_for_unique_idx on sandbox_for_unique(a,b);

Ich denke, man kann dann.

Nur für das Protokoll aber lasse ich meinen Absatz zu erklären, warum Oracle so verhält, wenn Sie einen einfachen eindeutigen Index für zwei Spalten:

Oracle wird nie akzeptieren zwei (1, null) Paare, wenn die Spalten eindeutig indiziert sind.

Ein Paar von 1 und einer Null, wird ein „indexierbaren“ Paar betrachtet. Ein Paar von zwei Nullen können nicht indiziert werden, deshalb ist es Ihnen, so viele null, null Paare können einfügen, wie Sie möchten.

(1, null) wird indiziert, weil 1 indiziert werden können. Das nächste Mal versuchen Sie einfügen (1, null) wieder 1 wird durch den Index und die eindeutige Einschränkung aufgehoben verletzt wird.

(null, null) indiziert ist nicht, weil es kein Wert indiziert werden soll. Deshalb ist es nicht die eindeutige Einschränkung verletzen.

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