Oracle
 sql >> Base de données >  >> RDS >> Oracle

Ajout de contraintes à l'aide d'une sous-requête d'une autre table

Une solution de contournement que vous pouvez faire est de créer une vue matérialisée contenant une requête identifiant les "mauvaises lignes".

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);

Essayons maintenant d'insérer quelques lignes :

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.

Cela s'est bien passé. Envoyons-nous maintenant un message :

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

Modifier, plus d'explications : Ok, cette requête (dans la définition de la vue matérialisée), identifie et compte tous les messages qui s'envoient à soi-même. Autrement dit, toutes les lignes qui violent la règle que vous avez énoncée.

select count(*) as bad_rows 
  from messages  m
  join receivers r using(message_id)
 where m.sender_id = r.receiver_id;

La requête doit donc renvoyer 0 lignes à tout moment, n'est-ce pas ? Ce que fait la vue matérialisée, c'est de se rafraîchir lorsque quelqu'un valide une opération DML sur les tables messages ou receivers . Donc en théorie, si quelqu'un insère un message à lui-même, la requête renverra bad_rows = 1 . Mais, j'ai également inclus une contrainte sur la vue matérialisée, indiquant que la seule valeur autorisée pour la colonne bad_rows est 0. Oracle ne vous laissera pas valider une transaction qui donne une autre valeur.

Donc, si vous regardez la deuxième paire d'instructions d'insertion, vous pouvez voir que j'ai réussi à insérer la ligne erronée dans les récepteurs, mais Oracle donne une violation de contrainte lorsque j'essaie de valider.