Едно решение, което можете да направите, е да създадете материализиран изглед, съдържащ заявка, идентифицираща „лошите редове“.
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);
Сега нека се опитаме да вмъкнем няколко реда:
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.
Това мина добре. Сега нека изпратим съобщение до себе си:
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
Редактиране, повече обяснение: Добре, тази заявка (в дефиницията на материализирания изглед) идентифицира и брои всички съобщения, които се изпращат до себе си. Тоест всички редове, които нарушават правилото, което посочихте.
select count(*) as bad_rows
from messages m
join receivers r using(message_id)
where m.sender_id = r.receiver_id;
Така че заявката трябва да връща 0 реда по всяко време, нали? Това, което прави материализираният изглед, е да се опреснява, когато някой извърши DML операция срещу таблиците messages
или receivers
. Така че на теория, ако някой вмъкне съобщение до себе си, заявката ще върне bad_rows = 1
. Но също така включих ограничение върху материализирания изглед, като казах, че единствената позволена стойност за колона bad_rows
е 0. Oracle няма да ви позволи да извършите транзакция, която дава друга стойност.
Така че, ако погледнете втората двойка изрази за вмъкване, можете да видите, че успях да вмъкна грешния ред в приемници, но Oracle дава нарушение на ограничението, когато се опитам да се ангажирам.