2010-07-30 16 views
6

Jak utworzyć klucz złożony na wielu kolumnach, z których jedna może mieć wartość, ale nie wartość zerową (lub pewną stałą wartość)?Tworzenie klucza złożonego na wielu kolumnach

Na przykład:

PK Loc_ID  Date    Time  Cancelled 
1   1   01/01/2010  10:00AM  YES 
2   1   01/01/2010  10:00AM  YES 
3   1   01/01/2010  10:00AM  null 
4   1   01/01/2010  10:00AM  null - Not Acceptable 

Dodanie czwartego rekordu powinna podnieść kompozytowy klucza błąd naruszenia.

+3

swoim przykładzie dane nie dają podstaw do odrzucenia wiersza z PK = 4, gdy nie odrzuca on również z PK = 2. –

+0

@ Jonathan Leffler - Pokonaj mnie. Miałem to samo pytanie. – Thomas

+0

Tak, PK = 2 jest OK. Anulowane może mieć dowolną wartość, ale nie zerową dwukrotność dla tego samego miejsca, daty i godziny. –

Odpowiedz

6

Więc co, co ma wymusić regułę gdzie tylko zapis nie może być anulowany dla każdej permutacji LOC_ID, data, godzina? Możemy to zrobić za pomocą unikalnego indeksu opartego na funkcjach.

To właśnie chcemy uniknąć:

SQL> select * from t34 
    2/

     PK  LOC_ID SOMEDATE SOMETIM CAN 
---------- ---------- ---------- ------- --- 
     1   1 01/01/2010 10:00AM YES 
     2   1 01/01/2010 10:00AM YES 
     3   1 01/01/2010 10:00AM 

SQL> insert into t34 
    2 values (4 , 1 , to_date('01/01/2010','DD/MM/YYYY') , '10:00AM', null) 
    3/

1 row created. 

SQL> 

Zbudujmy indeks egzekwować rządy

SQL> rollback 
    2/

Rollback complete. 

SQL> create unique index t34_uidx 
    2 on t34 (loc_id, somedate, some_time, nvl2(cancelled, pk, null)) 
    3/

Index created. 

SQL> 

Funkcja NVL2() jest specjalną formą przypadek, który zwraca drugi argument, jeśli pierwszy argument NIE jest równy NULL, w przeciwnym razie trzeci. Indeks używa PK col jako drugiego argumentu, ponieważ jest kluczem podstawowym, a więc unikalnym. Więc indeks pozwala zduplikowane wartości anulowanych chyba że są nieważne:

SQL> insert into t34 
    2 values (4 , 1 , to_date('01/01/2010','DD/MM/YYYY') , '10:00AM', null) 
    3/
insert into t34 values (4 , 1 , to_date('01/01/2010','DD/MM/YYYY') , '10:00AM', null) 
* 
ERROR at line 1: 
ORA-00001: unique constraint (APC.T34_UIDX) violated 


SQL> 
+0

wow !. To działa. Dzięki. –

0

Nie jestem pewien, czy jest to poprawne w Oracle, ale w Postgresql można to zrobić z częściowym indeksem wielokolumnowym na wartość null, z wyłączeniem kolumny, która ma wartość null.

CREATE UNIQUE INDEX idx_foo 
ON example (Loc_ID, Date, Time) 
WHERE canceled IS NULL 
+0

Nie, 'row2's' canceled' NIE JEST NULL, jest to częściowy indeks na NULL. Jestem prawie pewien, że możesz to zrobić w Oracle. Po prostu nie wiem jak i pomyślałem, że może to być dobry dodatek do odpowiedzi. –

+0

Dobrze. Widziałem to. Wierzę, że SQL 2008 również obsługuje to pojęcie, ale nie sądzę, że Oracle jeszcze. – Thomas

+0

Nie sądzę, myślę, że jest to prawdopodobnie SQL 99. Postgresql obsługuje to przez co najmniej 8 lat. –

1

Czy można to zrobić za pomocą unikalnego indeksu opartego na funkcjach? Coś jak:

create unique index ix on tb (
    loc_id, date, time, decode(cancelled, null, 1, null)); 
+0

nie będzie działać w wyroczni dla przypadków, gdy anulowane jest" tak "dla tej samej kombinacji loc_id, data , czas. –

+0

Wersja APC z nvl2() jest czystsza i działa bez zarzutu, a nie jest testowana.Jednak dla mnie samego, odwracam wartość zerową, efektywnie, co jest mniej więcej tak samo intencją; ale może powinienem zrobić "dekodowanie (anulowane, null, , pk)'. Gdzie magiczną wartością może być wszystko, czego nigdy nie będzie. Co jest oczywiście niebezpieczne samo w sobie, więc ... –

1

Jeśli reguła jest taka, że ​​tylko jeden NULL odwołany wartości dla konkretnej kombinacji LOC_ID, DATE_COL i TIME_COL:

SQL> create table EXAMPLE 
    2 ( PK  number  not null, 
    3  LOC_ID number  not null, 
    4  DATE_COL date   null, 
    5  TIME_COL varchar2(10) null, 
    6  CANCELLED varchar2(3) null, 
    7  constraint EXAMPLE_PK primary key (PK) 
    8 ); 

Table created. 

SQL> 
SQL> create unique index EXAMPLE_UK01 on EXAMPLE 
    2 (case when CANCELLED is null then LOC_ID else null end, 
    3  case when CANCELLED is null then DATE_COL else null end, 
    4  case when CANCELLED is null then TIME_COL else null end 
    5 ); 

Index created. 

SQL> 
SQL> INSERT INTO EXAMPLE VALUES 
    2 (1, 1, DATE '2010-01-01', '10:00AM', 'YES'); 

1 row created. 

SQL> 
SQL> INSERT INTO EXAMPLE VALUES 
    2 (2, 1, DATE '2010-01-01', '10:00AM', 'YES'); 

1 row created. 

SQL> 
SQL> INSERT INTO EXAMPLE VALUES 
    2 (3, 1, DATE '2010-01-01', '10:00AM', null); 

1 row created. 

SQL> 
SQL> INSERT INTO EXAMPLE VALUES 
    2 (4, 1, DATE '2010-01-01', '10:00AM', null); 
INSERT INTO EXAMPLE VALUES 
* 
ERROR at line 1: 
ORA-00001: unique constraint ([schema].EXAMPLE_UK01) violated 
Powiązane problemy