Una solución que puede hacer es crear una vista materializada que contenga una consulta que identifique las "filas incorrectas".
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);
Ahora intentemos insertar algunas filas:
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.
Eso salió bien. Ahora enviémonos un mensaje a mí mismo:
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
Editar, más explicación: Ok, esta consulta (en la definición de vista materializada), identifica y cuenta todos los mensajes que se envían a uno mismo. Es decir, todas las filas que violan la regla que pusiste.
select count(*) as bad_rows
from messages m
join receivers r using(message_id)
where m.sender_id = r.receiver_id;
Entonces, la consulta debe devolver 0 filas en todo momento, ¿verdad? Lo que hace la vista materializada es actualizarse cuando alguien realiza una operación DML en las tablas messages
. o receivers
. Entonces, en teoría, si alguien inserta un mensaje para sí mismo, la consulta devolvería bad_rows = 1
. Pero también he incluido una restricción en la vista materializada, diciendo que el único valor permitido para la columna bad_rows
es 0. Oracle no le permitirá realizar ninguna transacción que proporcione otro valor.
Entonces, si observa el segundo par de declaraciones de inserción, puede ver que logré insertar la fila errónea en los receptores, pero Oracle viola la restricción cuando intento confirmar.