Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Adicionando restrições usando subconsulta de outra tabela


Uma solução alternativa que você pode fazer é criar uma visualização materializada contendo uma consulta identificando as "linhas inválidas".
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);

Agora vamos tentar inserir algumas linhas:
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.

Correu bem. Agora vamos enviar uma mensagem para mim mesmo:
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, mais explicações: Ok, esta consulta (na definição da visão materializada), identifica e conta todas as mensagens que estão sendo enviadas para si mesmo. Ou seja, todas as linhas que violam a regra que você indicou.
select count(*) as bad_rows 
  from messages  m
  join receivers r using(message_id)
 where m.sender_id = r.receiver_id;

Portanto, a consulta deve retornar 0 linhas o tempo todo, certo? O que a visualização materializada faz é se atualizar quando alguém confirma uma operação DML nas tabelas messages ou receivers . Então, em teoria, se alguém inserir uma mensagem para si mesma, a consulta retornará bad_rows = 1 . Mas também incluí uma restrição na visualização materializada, dizendo que o único valor permitido para a coluna bad_rows é 0. O Oracle não permitirá que você confirme nenhuma transação que dê outro valor.

Portanto, se você observar o segundo par de instruções de inserção, poderá ver que consegui inserir a linha errada nos receptores, mas o Oracle fornece uma violação de restrição quando tento confirmar.