2010-07-30 34 views
6

¿Cómo puedo crear una clave compuesta en varias columnas, una de las cuales puede tener algún valor pero no nulo (o algún valor constante)?Cómo crear una clave compuesta en varias columnas

Por ejemplo:

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 

inserción del cuarto registro debe plantear un error de violación clave compuesta.

+3

Su ejemplo los datos no dan ninguna base para rechazar la fila con PK = 4 cuando tampoco rechaza la que tiene PK = 2. –

+0

@Jonathan Leffler - Pásame a ello. Tenía la misma pregunta. – Thomas

+0

Sí, PK = 2 está bien. Cancelado puede tener cualquier valor, pero no nulo, repetir dos veces para el mismo locid, fecha y hora. –

Respuesta

6

Así que lo que lo que ha de cumplir una regla que sólo registro no puede ser cancelada por cualquier permutación dada de LOC_ID, fecha, hora? Podemos hacer esto con un índice único basado en función.

Esto es lo que queremos evitar:

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> 

Vamos a construir un índice para aplicar la regla

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> 

La función NVL2() es una forma especial de CASE que devuelve el segundo argumento si el El primer argumento NO es NULO sino el tercero. El índice utiliza la columna PK como segundo argumento porque es la clave principal y, por lo tanto, única. Por lo que el índice permite valores duplicados de CANCELADA a menos que sean nulos:

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 !. Esto funciona. Gracias. –

0

No estoy seguro de que esto sea válido en Oracle, pero en Postgresql podría hacer esto con un índice de columnas parciales parcial en nulo, excluyendo la columna que es nula.

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

No, 'rowled'' cancelled' IS NOT NULL, este es un índice parcial en NULL. Estoy bastante seguro de que puedes hacer esto en Oracle. No sé cómo, y pensé que podría ser una buena adición a las respuestas. –

+0

Derecha. Ví eso. Creo que SQL 2008 también apoya esta idea, pero no creo que Oracle lo haga todavía. – Thomas

+0

No lo creo, creo que esto es probablemente SQL 99. Postgresql lo ha respaldado durante al menos 8 años. –

1

¿Se podría hacer esto con un único índice basado en función? Algo así como:

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

no funcionará en Oracle para los casos en que se cancela es 'sí' para la misma combinación de loc_id, fecha , hora. –

+0

La versión de APC con nvl2() es más limpia, y claramente funciona, mientras que esto no se ha probado.Solo por mi propio bien, estoy invirtiendo el nulo, efectivamente, que tiene más o menos la misma intención; pero quizás debería haber hecho 'decode (cancelado, nulo, , pk)'. Donde el valor mágico puede ser cualquier cosa 'pk' nunca sería. Lo cual es claramente peligroso en sí mismo, así que ... –

1

Si la regla es que sólo un valor NULL cancelado por una combinación particular de LOC_ID, DATE_COL y 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 
Cuestiones relacionadas