Oracle
 sql >> Database >  >> RDS >> Oracle

Aggiunta di vincoli tramite sottoquery da un'altra tabella

Una soluzione alternativa che puoi fare è creare una vista materializzata contenente una query che identifica le "righe non valide".

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

Ora proviamo a inserire alcune righe:

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.

È andata bene. Ora mandiamo un messaggio a me stesso:

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

Modifica, ulteriori spiegazioni: Ok, questa query (nella definizione della vista materializzata), identifica e conta tutti i messaggi che vengono inviati a se stessi. Ovvero, tutte le righe che violano la regola che hai indicato.

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

Quindi la query dovrebbe restituire 0 righe in ogni momento, giusto? Ciò che fa la vista materializzata è aggiornarsi quando qualcuno esegue un'operazione DML sulle tabelle messages o receivers . Quindi, in teoria, se qualcuno inserisce un messaggio a se stesso, la query restituirebbe bad_rows = 1 . Ma ho anche incluso un vincolo sulla vista materializzata, dicendo che l'unico valore consentito per la colonna bad_rows è 0. Oracle non ti consentirà di eseguire alcuna transazione che dia un altro valore.

Quindi, se guardi la seconda coppia di istruzioni di inserimento, puoi vedere che sono riuscito a inserire la riga errata nei ricevitori, ma Oracle fornisce una violazione del vincolo quando provo a eseguire il commit.