En løsning, du kan gøre, er at oprette en materialiseret visning, der indeholder en forespørgsel, der identificerer de "dårlige rækker".
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);
Lad os nu prøve at indsætte nogle rækker:
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.
Det gik godt. Lad os nu sende en besked til mig selv:
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
Rediger, mere forklaring: Ok, denne forespørgsel (i den materialiserede visningsdefinition) identificerer og tæller alle de beskeder, der bliver sendt til en selv. Det vil sige alle de rækker, der overtræder den regel du har angivet.
select count(*) as bad_rows
from messages m
join receivers r using(message_id)
where m.sender_id = r.receiver_id;
Så forespørgslen burde altid returnere 0 rækker, ikke? Hvad den materialiserede visning gør, er at opdatere sig selv, når nogen foretager en DML-operation mod tabellernes messages
eller receivers
. Så i teorien, hvis nogen indsætter en besked til sig selv, ville forespørgslen returnere bad_rows = 1
. Men jeg har også inkluderet en begrænsning på den materialiserede visning, idet jeg siger, at den eneste tilladte værdi for kolonnen bad_rows
er 0. Oracle vil ikke lade dig begå nogen transaktion, der giver en anden værdi.
Så hvis du ser på det andet par insert-udsagn, kan du se, at det er lykkedes mig at indsætte den fejlagtige række i modtagere, men Oracle giver en overtrædelse af begrænsningen, når jeg forsøger at begå.