Virtual Column
July 10, 2012 2 Comments
Stated requirement from the forum:
To have a foreign key to a parent table only when the status in the child table is a certain value whilst allowing for rubbish and NULLs in the child’s fk column when the status is not ACTIVE.
We can do this with a virtual column.
Here’s how.
Setup:
SQL> create table t1 2 (col1 number primary key); Table created. SQL> insert into t1 values (1); 1 row created. SQL> create table t2 2 (col1 number primary key 3 ,status varchar2(12) not null 4 ,col2 number 5 ,col2v number generated always as (case when status = 'ACTIVE' then col2 end) VIRTUAL 6 ,constraint t2_fk foreign key (col2v) references t1(col1) 7 ,constraint t2_ck check (status IN ('ACTIVE','INACTIVE') and (status = 'INACTIVE' or col2 IS NOT NULL))); Table created. SQL> create sequence s1; Sequence created. SQL>
First requirement – insert an active row into T2 with a valid FK in col2:
SQL> insert into t2 (col1, status, col2) values (s1.nextval,'ACTIVE',1); 1 row created. SQL>
Second requirement – ensure that active rows have a valid foreign key:
SQL> insert into t2 (col1, status, col2) values (s1.nextval,'ACTIVE',-99); insert into t2 (col1, status, col2) values (s1.nextval,'ACTIVE',-99) * ERROR at line 1: ORA-02291: integrity constraint (RIMS.T2_FK) violated - parent key not found SQL>
Third requirement – allow inactive rows to have rubbish in col2 (for historic data quality reasons?):
SQL> insert into t2 (col1, status, col2) values (s1.nextval,'INACTIVE',-99); 1 row created. SQL>
Fourth requirement – prevent NULL foreign key values for active rows:
SQL> insert into t2 (col1, status, col2) values (s1.nextval,'ACTIVE',NULL); insert into t2 (col1, status, col2) values (s1.nextval,'ACTIVE',NULL) * ERROR at line 1: ORA-02290: check constraint (RIMS.T2_CK) violated SQL>
Just some minor tweaks on the requirements which came up in this forum question…
Firstly, let’s say it’s not just statuses of ACTIVE/INACTIVE. We want to ensure that data quality for ACTIVE is protected but there’s a whole bunch of other statuses not just INACTIVE.
So, change the check constraint:
SQL> alter table t2 drop constraint t2_ck; Table altered. SQL> alter table t2 add constraint t2_ck check (NOT (status = 'ACTIVE' and col2 IS NULL)); Table altered. SQL> insert into t2 (col1, status, col2) values (s1.nextval,'WHO CARES',-75); 1 row created. SQL>
Or what if active statuses can insert a valid foreign key or NULL?
Just lose the check constraint altogether:
SQL> alter table t2 drop constraint t2_ck; Table altered. SQL> insert into t2 (col1, status, col2) values (s1.nextval,'ACTIVE',NULL); 1 row created. SQL> insert into t2 (col1, status, col2) values (s1.nextval,'WHO KNOWS',-75); 1 row created. SQL> SQL> insert into t2 (col1, status, col2) values (s1.nextval,'INACTIVE',-99); 1 row created. SQL> SQL> insert into t2 (col1, status, col2) values (s1.nextval,'ACTIVE',-99); insert into t2 (col1, status, col2) values (s1.nextval,'ACTIVE',-99) * ERROR at line 1: ORA-02291: integrity constraint (RIMS.T2_FK) violated - parent key not found SQL> SQL> insert into t2 (col1, status, col2) values (s1.nextval,'ACTIVE',1); 1 row created. SQL>
Neat!
Thanks.
Pingback: Conditional uniqueness | OraStory