The other day I was asked about the following: “We have a table VARIABLE and Variable entity. A new requirement is to restrict availability of a limited number of these entities to chosen customers.” I replied: “The volumes?” And the response was: “Quite low – now around 70 rows, it might slightly exceed 100 in the future. No LOBs.”
The developer continued: “So far we have been doing such filtering by applying conditions on FK columns referencing COMPANY table. We want to do the same here. But there’s a small difference: a Variable can be either visible to all customers (no restrictions) or to 1 or more chosen customers. So, an intersection table is quite a natural choice. E.g. COMPANY_VARIABLE table with 2 columns only: FK_VARIABLE, FK_COMPANY. Only FK_VARIABLE would be not null. So, the FK referencing COMPANY table would be optional.
I’d like to avoid keeping many rows in the new table for a variable without visibility restrictions. For such variables it would be enough to have only 1 row with null in FK_COMPANY. Otherwise, it would be
- too cumbersome,
- error-prone (when adding a new company or a no-restriction variable, you’d always have to add many rows to the intersection table)
- and taking DB storage unnecessarily.
In turn, if a variable were to be restricted, we would add a new (VARIABLE_ID, COMPANY_ID) row to the table.”
My first thought was: “You can not do this, because you need a composite primary key on the two columns FK_VARIABLE and FK_COMPANY in the COMPANY_VARIABLE table. And a column which is a part of the primary key will per definition be NOT NULL”. Of course you can introduce a surrogate key and use this as a primary key, and then create a unique index on the two columns. Then you are aloud to define the FK_COMPANY as nullable.
But could I do the same thing without a surrogate key? Well I thought of one solution using an invisible virtual column. I’m not saying that this is a good solution. Then I would have to test it a little more first (Note! If you have some experience with something similar, please write a comment to this blog post).
So here is the idea:
--------------------------------------------
-- Table: CUSTOMER
--------------------------------------------
create table customer (id number);
alter table customer add constraint customer_pk primary key (id);
insert into customer values ('1001');
insert into customer values ('1002');
--------------------------------------------
-- Table: VARIABLE
--------------------------------------------
create table variable (id number, variable varchar2(30));
alter table variable add constraint variable_pk primary key (id);
insert into variable values (1, 'Test');
insert into variable values (2, 'Test 2');
insert into variable values (3, 'Test 3');
--------------------------------------------
-- Table: CUSTOMER_VARIABLE
--------------------------------------------
create table customer_variable (fk_customer number, fk_variable number );
alter table customer_variable add (fk_customer_all number as (case when fk_customer is null then -1 else fk_customer end ));
alter table customer_variable add (
constraint customer_variable_pk primary key (fk_customer_all, fk_variable),
constraint custvar_customer_fk foreign key (fk_customer) references customer (id),
constraint custvar_variable_fk foreign key (fk_variable) references variable (id)
);
alter table customer_variable modify (fk_customer_all invisible);
insert into customer_variable values (null, 1);
insert into customer_variable values (null, 2);
insert into customer_variable values (1001, 1);
insert into customer_variable values (1002, 2);
insert into customer_variable values (1002, 3);
-- These should fail (constraint violations)
insert into customer_variable values (null, 1);
insert into customer_variable values (1002, 2);
commit;
-- find all valid variables for a customer
with my_variable as (
select cv.*, rank() over (partition by fk_variable order by fk_customer nulls last) rn
from customer_variable cv
where fk_customer=:b1 or fk_customer is null
)
select fk_variable
from my_variable
where rn=1;
Post a Comment