sql >> Database >  >> RDS >> Oracle

Beperkingen toevoegen met behulp van subquery's uit een andere tabel

Een oplossing die u kunt doen, is een gematerialiseerde weergave maken met een query die de "slechte rijen" identificeert.

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

Laten we nu proberen enkele rijen in te voegen:

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.

Dat ging goed. Laten we nu een bericht naar mezelf sturen:

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

Bewerken, meer uitleg: Ok, deze query (in de gematerialiseerde weergave-definitie), identificeert en telt alle berichten die naar jezelf worden verzonden. Dat wil zeggen, alle rijen die schenden de door u aangegeven regel.

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

Dus de query zou te allen tijde 0 rijen moeten retourneren, toch? Wat de gematerialiseerde weergave doet, is zichzelf vernieuwen wanneer iemand een DML-bewerking uitvoert tegen de tabellen messages of receivers . Dus in theorie, als iemand een bericht voor zichzelf invoegt, zou de zoekopdracht bad_rows = 1 opleveren . Maar ik heb ook een beperking toegevoegd aan de gerealiseerde weergave, door te zeggen dat de enige toegestane waarde voor kolom bad_rows is 0. Oracle laat u geen transactie uitvoeren die een andere waarde geeft.

Dus als je naar het tweede paar insert-statements kijkt, kun je zien dat ik erin geslaagd ben de foutieve rij in ontvangers in te voegen, maar Oracle geeft een schending van de beperking wanneer ik probeer te committen.




  1. Slow bulk insert voor tafel met veel indexen

  2. Databaseschema, automatisch verhogen

  3. Meer dan één AutoIncrement per tabel hebben

  4. MySQL voegt geen backslash in