sql >> Base de Datos >  >> RDS >> Oracle

Agregar restricciones usando una subconsulta de otra tabla

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.