Search code examples
sqloracleconstraintsunique-constraintcomposite-key

How to give a unique constraint to a combination of a column and a fixed value in Oracle?


I have a table with with 3 columns: A (number), B (number) and C (boolean).

I would need to create a rule that would prevent records from being created with columns A and B and with C equal to true. For example.

This would be allowed:

A  B  C
1  2  true
1  2  false
1  2  false

But this, no:

A  B  C
1  2  true
1  2  true
1  2  false

Solution

  • Slight variation on MarmiteBomber's approach, to avoid concatenating the values (which could cause accidental clashes with non-integer values):

    create table t (a number, b number, c varchar2(5),
      constraint t_chk check (c in ('true', 'false'))
    );
    
    create unique index t_unq
    on t (case when c = 'true' then a end, case when c = 'true' then b end);
    
    insert into t(a,b,c) values (1,2,'true');
    
    1 row inserted.
    
    insert into t(a,b,c) values (1,2,'false');
    
    1 row inserted.
    
    insert into t(a,b,c) values (1,2,'false');
    
    1 row inserted.
    
    insert into t(a,b,c) values (1,2,'true');
    
    ORA-00001: unique constraint (MY_SCHEMA.T_UNQ) violated
    
    select * from t;
    
             A          B C    
    ---------- ---------- -----
             1          2 true 
             1          2 false
             1          2 false
    

    Quick example of why non-integers (if they can exist) might be a problem:

    create unique index uq_true on test(case when c = 'true' then a||'.'||b end);
    
    insert into test(a,b,c) values (1.1, 2,'true');
    
    1 row inserted.
    
    insert into test(a,b,c) values (1, 1.2,'true');
    
    ORA-00001: unique constraint (MY_SCHEMA.UQ_TRUE) violated
    
    select * from test;
    
             A          B C    
    ---------- ---------- -----
           1.1          2 true 
    

    ... because for both '1.1' ||'.'|| '2' and '1' ||'.'|| '1.2' resolve to the same string, '1.1.2'.

    This can also be a problem when combining string values rather than numbers. In either case you can avoid it by using a delimiter which cannot exist in either value; harder to do with strings, but with numbers any punctuation other than a period (or comma to be safe) would probably do - unless someone has a weird setting for nls_numeric_characters...