One work-around you can do is to create a materialized view containing a query identifying the "bad rows".
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);
Now let's try to insert some rows:
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.
That went well. Now let's send a message to myself:
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
Edit, more explanation:
Ok, this query (in the materialized view definition), identifies and counts all the messages that are being sent to oneself. That is, all the rows that violate the rule you stated.
select count(*) as bad_rows
from messages m
join receivers r using(message_id)
where m.sender_id = r.receiver_id;
So the query should return 0 rows at all times, right?
What the materialized view does, is to refresh itself when anyone commits a DML operation against the tables messages
or receivers
. So in theory, if someone inserts a message to herself, the query would return bad_rows = 1
. But, I've also included a constraint on the materialized view, saying that the only allowed value for column bad_rows
is 0. Oracle will not let you commit any transaction that gives another value.
So if you look at the second pair of insert statements, you can see that I've managed to insert the erroneous row in receivers, but Oracle gives a constraint violation when I try to commit.