一般に、この種の制約をトリガーに適用することはできません。制約を使用する必要があります。
トリガーを使用しようとした場合に直面する問題は、通常、「テーブルの変更」例外が発生することです。一般に、テーブルAの行レベルのトリガー(つまり、properties
)テーブルAをクエリできません。パッケージを作成し、そのパッケージにコレクションを作成し、beforeステートメントトリガーでコレクションを初期化し、行レベルのトリガーでコレクションに挿入または更新されるキーを書き込むことで、この問題を回避できます。 、次に、afterステートメントトリガーでコレクションの要素を反復処理し、テーブルに対して適切なDMLを発行します。ただし、これには非常に多くの可動部分と非常に多くの複雑さが含まれます(ただし、11gを使用している場合は複雑さが軽減され、代わりに複合トリガーを使用できます)。
さらに、トリガーを使用しようとすると、マルチユーザー環境で問題が発生します。ユーザーAが1つのセッションに行を挿入し、ユーザーAがコミットする前にユーザーBが別のセッションに重複行を挿入した場合、どちらのセッションのトリガーも重複行を検出しません。挿入をテーブルにシリアル化するために親テーブルの行を明示的にロックすることで、この種の問題を回避できる可能性があります(意図的にアプリケーションの速度を低下させ、スケーラビリティを低下させます)。しかし、制約ははるかに効率的で実用的な解決策になります。
とはいえ、INSERT ... VALUES
を使用して単一行の挿入のみを行う場合 構文を設定し、単一のセッションに制限すると、トリガーが機能しているように見えます
SQL> ed
Wrote file afiedt.buf
1 create table Properties(
2 idProperties number(10) NOT NULL,
3 Address_FK number(20),
4 Ownership_FK number(20)
5* )
SQL> /
Table created.
SQL> CREATE OR REPLACE TRIGGER Check_Duplicate
2 before insert or update on properties
3 FOR each ROW
4
5 declare
6 v_dup number;
7
8 begin
9 select count(idProperties) INTO v_dup from properties where Address_FK=
:NEW.Address_FK and
10 Ownership_FK=:NEW.Ownership_FK;
11
12 if v_dup > 0 then
13 Raise_Application_Error (-20100, 'This property already exists. The inse
rt is cancelled.');
14 end if;
15 end;
16 /
Trigger created.
SQL> insert into properties values( 1, 10, 100 );
1 row created.
SQL> insert into properties values( 2, 10, 100 );
insert into properties values( 2, 10, 100 )
*
ERROR at line 1:
ORA-20100: This property already exists. The insert is cancelled.
ORA-06512: at "SCOTT.CHECK_DUPLICATE", line 9
ORA-04088: error during execution of trigger 'SCOTT.CHECK_DUPLICATE'