Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Hinzufügen von Einschränkungen mithilfe einer Unterabfrage aus einer anderen Tabelle

Eine Problemumgehung, die Sie durchführen können, besteht darin, eine materialisierte Ansicht zu erstellen, die eine Abfrage enthält, die die "fehlerhaften Zeilen" identifiziert.

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

Versuchen wir nun, einige Zeilen einzufügen:

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.

Das lief gut. Lassen Sie uns jetzt eine Nachricht an mich selbst senden:

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

Bearbeiten, weitere Erklärung: Ok, diese Abfrage (in der materialisierten Ansichtsdefinition) identifiziert und zählt alle Nachrichten, die an einen selbst gesendet werden. Das heißt, alle Zeilen, die verletzen die Regel, die Sie angegeben haben.

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

Die Abfrage sollte also immer 0 Zeilen zurückgeben, richtig? Die materialisierte Ansicht aktualisiert sich selbst, wenn jemand eine DML-Operation für die messages der Tabelle durchführt oder receivers . Wenn also jemand eine Nachricht an sich selbst einfügt, würde die Abfrage bad_rows = 1 zurückgeben . Aber ich habe auch eine Einschränkung für die materialisierte Ansicht eingefügt, die besagt, dass der einzige zulässige Wert für die Spalte bad_rows ist ist 0. Oracle lässt Sie keine Transaktionen ausführen, die einen anderen Wert angeben.

Wenn Sie sich also das zweite Paar von Insert-Anweisungen ansehen, können Sie sehen, dass ich es geschafft habe, die fehlerhafte Zeile in Empfänger einzufügen, aber Oracle gibt eine Einschränkungsverletzung aus, wenn ich versuche, mich zu verpflichten.