Domanda

Ecco un piccolo esperimento mi sono imbattuto in un database Oracle (10g). A parte la convenienza implementazione (Oracle), non riesco a capire perché alcuni inserimenti sono accettati e altri respinti.

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

Supponendo che ha senso avere di tanto in tanto alcune righe con alcuni valori di colonna sconosciuta, mi vengono in mente due possibili casi d'uso che coinvolgono duplicati impedendo:
 1. Voglio respingere i duplicati, ma accetta quando il valore di ogni colonna vincolata è sconosciuta.
 2. Voglio respingere i duplicati, anche nei casi in cui il valore di una colonna vincolata è sconosciuta.

A quanto pare Oracle implementa qualcosa di diverso però:
 3. Rifiuta duplicati, ma accetta (solo) quando tutti i valori delle colonne vincolate sono sconosciuti.

riesco a pensare a modi di fare uso di implementazione di Oracle per arrivare a utilizzare caso (2) - per esempio, hanno un valore speciale per "sconosciuto", e rendere le colonne non annullabili. Ma io non riesco a capire come raggiungere usare caso (1).

In altre parole, come posso ottenere Oracle ad agire in questo modo?

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
È stato utile?

Soluzione 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);

Un indice funzionale! Fondamentalmente ho solo bisogno di assicurarsi che tutte le tuple che voglio ignorare (vale a dire - accetta) vengono tradotti a tutti i null. Brutto, ma non intromettermi brutto. Funziona, se lo desideri.

capito con l'aiuto di una soluzione ad un'altra domanda: Come per vincolare una tabella di database in modo che solo una riga può avere un particolare valore in una colonna?

Quindi, andare lì e dare Tony Andrews sottolinea anche. :)

Altri suggerimenti

Prova un indice basato su funzioni:

creare unico sandbox_idx indice su sandbox (caso in cui un IS NULL ALLORA NULL quando B è NULL THEN ELSE NULL un || '' || b END);

Ci sono altri modi per la pelle questo gatto, ma questo è uno di loro.

Io non sono un ragazzo di Oracle, ma ecco un'idea che dovrebbe funzionare, se è possibile includere una colonna calcolata in un indice in Oracle.

Aggiungi una colonna aggiuntiva al vostro tavolo (e il vostro indice UNIQUE) che viene calcolato come segue: è NULL se entrambi a e b sono non-NULL, ed è chiave primaria della tabella altrimenti. Io chiamo questo ulteriore colonna "nullbuster" per ovvi motivi.

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);

Ho dato questo esempio un certo numero di volte intorno al 2002 o giù di lì nel microsoft.public.sqlserver.programming gruppo Usenet. È possibile trovare le discussioni se si cerca groups.google.com per la parola "nullbuster". Il fatto che si sta utilizzando Oracle non dovrebbe importa molto.

.

P.S In SQL Server, questa soluzione è praticamente sostituito da indici filtrati:

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

Il filo si fa riferimento suggerisce che Oracle non ti dà questa opzione. Ha, inoltre, non hanno la possibilità di una vista indicizzata, che è un'altra alternativa?

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);

Credo che si può poi.

Per la cronaca, però, lascio il mio punto di spiegare il motivo per cui Oracle si comporta come che se si dispone di un semplice indice univoco su due colonne:

Oracle non accetterà due coppie (1, NULL) se le colonne sono indicizzate univoco.

Una coppia di 1 e un nullo, viene considerata una coppia "indicizzabile". Una coppia di due nulli non possono essere indicizzati, è per questo che consente di inserire il maggior numero nullo, coppie nulli come ti piace.

(1, nullo) viene indicizzato perché 1 può essere indicizzato. La prossima volta che si tenta di inserire (1, null) ancora una volta, 1 viene prelevato dal l'indice e il vincolo univoco è violata.

(null, null) non viene indicizzato perché non v'è alcun valore da indicizzare. Ecco perché non viola il vincolo univoco.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top