Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional unique constraint with multiple fields in oracle db

I have this table:

XPTO_TABLE (id, obj_x, date_x, type_x, status_x)

I wanna create a unique constraint that applies to the fields (obj_x, date_x, type_x) only when status_x <> 5.

I have tried to create this one but Oracle says:

line 1: ORA-00907: missing right parenthesis
CREATE UNIQUE INDEX UN_OBJ_DT_TYPE_STATUS
ON XPTO_TABLE(
    (CASE
         WHEN STATUS_X <> 5
         THEN
             (OBJ_X,
              TO_CHAR (DATE_X, 'dd/MM/yyyy'),
              TYPE_X)
         ELSE
             NULL
     END));

What's the correct syntax ?

like image 668
James Freitas Avatar asked Jan 13 '14 10:01

James Freitas


People also ask

How do you enforce conditional unique multiple columns?

We implement this using CASE WHEN ensconced in the CREATE UNIQUE INDEX statement: SQL> -- Conditional unique index on multiple columns SQL> create unique index demo_fbi_idx 2 on demo_fbi 3 (case when active_flag = 'Y' then 4 col1 else null end, 5 case when active_flag = 'Y' then 6 col2 else null end); Index created.

Can we create unique key constraint on multiple columns?

You can define a UNIQUE constraint at the column or the table level. Only at the table level, you can define a UNIQUE constraint across multiple columns. Once a UNIQUE constraint is defined, if you attempt to insert or update a value that already exists in the column, SQLite will issue an error and abort the operation.

How do you create a composite unique constraint in Oracle?

The syntax for creating a unique constraint using an ALTER TABLE statement in Oracle is: ALTER TABLE table_name ADD CONSTRAINT constraint_name UNIQUE (column1, column2, ... column_n); table_name.

How many columns can hold unique constraints?

Defining Composite Unique Keys Oracle creates an index on the columns of a unique key, so a composite unique key can contain a maximum of 16 columns.


3 Answers

@jamesfrj: it looks like you are trying to ensure that your table should contain only one record for which status <>5.

You can try creating a unique functional index by concatenating the columns, as given below

      create table XPTO_TABLE (id number, 
                            obj_x varchar2(20),
                            date_x date,
                            type_x varchar2(20),
                            status_x varchar2(20)                              
                           );

      create unique index xpto_table_idx1 on XPTO_TABLE(case when status_x <>'5'  THEN              obj_x||date_x||type_x||STATUS_x ELSE null END);

Hope it helps

Vishad

like image 120
vishad Avatar answered Oct 19 '22 07:10

vishad


Under Oracle 11, you can create a bunch of virtual columns that get non-NULL value only when STATUS_X is 5, and then make them unique:

CREATE TABLE XPTO_TABLE (
  ID INT PRIMARY KEY,
  OBJ_X INT,
  DATE_X DATE,
  TYPE_X VARCHAR2(50),
  STATUS_X INT,
  OBJ_U AS (CASE STATUS_X WHEN 5 THEN OBJ_X ELSE NULL END),
  DATE_U AS (CASE STATUS_X WHEN 5 THEN DATE_X ELSE NULL END),
  TYPE_U AS (CASE STATUS_X WHEN 5 THEN TYPE_X ELSE NULL END),
  UNIQUE (OBJ_U, DATE_U, TYPE_U)
);

You can freely insert duplicates, as long as STATUS_X is not 5:

INSERT INTO XPTO_TABLE (ID, OBJ_X, DATE_X, TYPE_X, STATUS_X) VALUES (1, 1, '1-JAN-2014', 'foo', 4);
INSERT INTO XPTO_TABLE (ID, OBJ_X, DATE_X, TYPE_X, STATUS_X) VALUES (2, 1, '1-JAN-2014', 'foo', 4);

But trying to insert a duplicate when STATUS_X is 5 fails:

INSERT INTO XPTO_TABLE (ID, OBJ_X, DATE_X, TYPE_X, STATUS_X) VALUES (3, 1, '1-JAN-2014', 'foo', 5);
INSERT INTO XPTO_TABLE (ID, OBJ_X, DATE_X, TYPE_X, STATUS_X) VALUES (4, 1, '1-JAN-2014', 'foo', 5);

Error report -
SQL Error: ORA-00001: unique constraint (IFSAPP.SYS_C00139498) violated
00001. 00000 -  "unique constraint (%s.%s) violated"
*Cause:    An UPDATE or INSERT statement attempted to insert a duplicate key.
           For Trusted Oracle configured in DBMS MAC mode, you may see
           this message if a duplicate entry exists at a different level.
*Action:   Either remove the unique restriction or do not insert the key.
like image 25
Branko Dimitrijevic Avatar answered Oct 19 '22 07:10

Branko Dimitrijevic


Because the CREATE UNIQUE INDEX is expecting only a value you can concatenate the columns as follow

CREATE UNIQUE INDEX UN_OBJ_DT_TYPE_STATUS
ON XPTO_TABLE(
(CASE
     WHEN STATUS_X <> 5
         THEN OBJ_X || TO_CHAR (DATE_X, 'dd/MM/yyyy') || TYPE_X
     ELSE
         NULL
 END));
like image 36
Marco Montel Avatar answered Oct 19 '22 09:10

Marco Montel