Jednym z obejść, które możesz zrobić, jest utworzenie zmaterializowanego widoku zawierającego zapytanie identyfikujące „złe wiersze”.
create table messages(
message_id number not null
,sender_id varchar2(20) not null
,primary key(message_id)
);
create table receivers(
message_id number not null
,receiver_id varchar2(20) not null
,primary key(message_id,receiver_id)
,foreign key(message_id) references messages(message_id)
);
create materialized view log
on receivers with primary key, rowid including new values;
create materialized view log
on messages with primary key, rowid (sender_id) including new values;
create materialized view mv
refresh fast on commit
as
select count(*) as bad_rows
from messages m
join receivers r using(message_id)
where m.sender_id = r.receiver_id;
alter materialized view mv
add constraint dont_send_to_self check(bad_rows = 0);
Teraz spróbujmy wstawić kilka wierszy:
SQL> insert into messages(message_id, sender_id) values(1, 'Ronnie');
1 row created.
SQL> insert into receivers(message_id, receiver_id) values(1, 'Mayank Sharma');
1 row created.
SQL> commit;
Commit complete.
To poszło dobrze. Teraz wyślijmy wiadomość do mnie:
SQL> insert into messages(message_id, sender_id) values(2, 'Ronnie');
1 row created.
SQL> insert into receivers(message_id, receiver_id) values(2, 'Ronnie');
1 row created.
SQL> commit;
commit
*
ERROR at line 1:
ORA-12008: error in materialized view refresh path
ORA-02290: check constraint (RNBN.DONT_SEND_TO_SELF) violated
Edytuj, więcej wyjaśnień: Ok, to zapytanie (w definicji widoku zmaterializowanego) identyfikuje i zlicza wszystkie wiadomości, które są do nas wysyłane. To znaczy wszystkie wiersze, które naruszają zasada, którą podałeś.
select count(*) as bad_rows
from messages m
join receivers r using(message_id)
where m.sender_id = r.receiver_id;
Zatem zapytanie powinno zawsze zwracać 0 wierszy, prawda? Widok zmaterializowany odświeża się, gdy ktoś zatwierdzi operację DML na tabelach messages
lub receivers
. Więc teoretycznie, jeśli ktoś wstawi do siebie wiadomość, zapytanie zwróci bad_rows = 1
. Ale dodałem również ograniczenie na widok zmaterializowany, mówiąc, że jedyna dozwolona wartość dla kolumny bad_rows
wynosi 0. Oracle nie pozwoli na dokonanie transakcji, która daje inną wartość.
Więc jeśli spojrzysz na drugą parę instrukcji INSERT, zobaczysz, że udało mi się wstawić błędny wiersz do odbiorników, ale Oracle narusza ograniczenia, gdy próbuję zatwierdzić.