実行できる回避策の1つは、「不良行」を識別するクエリを含むマテリアライズドビューを作成することです。
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);
それでは、いくつかの行を挿入してみましょう:
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.
それはうまくいった。それでは、自分にメッセージを送信しましょう:
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
編集、詳細説明: OK、このクエリ(マテリアライズドビュー定義内)は、自分に送信されているすべてのメッセージを識別してカウントします。つまり、違反するすべての行 あなたが述べたルール。
select count(*) as bad_rows
from messages m
join receivers r using(message_id)
where m.sender_id = r.receiver_id;
したがって、クエリは常に0行を返す必要がありますか?マテリアライズドビューが行うことは、誰かがテーブルのmessages
に対してDML操作をコミットしたときに、それ自体を更新することです。 またはreceivers
。したがって、理論的には、誰かが自分自身にメッセージを挿入すると、クエリはbad_rows = 1
を返します。 。ただし、列bad_rows
に許可される唯一の値として、マテリアライズドビューに制約を含めました。 は0です。Oracleは、別の値を与えるトランザクションをコミットすることを許可しません。
したがって、挿入ステートメントの2番目のペアを見ると、誤った行をレシーバーに挿入できたことがわかりますが、コミットしようとするとOracleから制約違反が発生します。