よし、2点。まず、 SELECT INTO ... でNO_DATA_FOUND例外が発生する危険があります。この例外を発生させると、挿入全体が強制終了されます。次に、挿入全体を停止する例外を発生させています。
例外を発生させるのではなく、ブロックされたテーブルにある IDを無視する必要があります。元のアイデアに従うには、 NO_DATA_FOUND 例外を利用して、何も見つからない場合にのみ挿入する方法があります。テーブルにビューを作成し、これにINSTEAD OF トリガーを定義します。
ただし、この方法は使用しません(以下を参照)
テスト環境をセットアップした場合:
SQL> create table tmp_test ( id number );
Table created.
SQL> create table tmp_blocked ( id number );
Table created.
SQL> insert into tmp_blocked values (3);
1 row created.
次に、以下を使用できます。
SQL> create or replace view v_tmp_test as select * from tmp_test;
View created.
SQL> create or replace trigger tr_test
2 instead of insert on v_tmp_test
3 for each row
4
5 declare
6
7 l_id tmp_test.id%type;
8
9 begin
10
11 select id into l_id
12 from tmp_blocked
13 where id = :new.id;
14
15 exception when no_data_found then
16 insert into tmp_test values (:new.id);
17 end;
18 /
Trigger created.
SQL> show error
No errors.
SQL> insert into v_tmp_test
2 select level
3 from dual
4 connect by level <= 3;
3 rows created.
SQL> select * from tmp_test;
ID
----------
1
2
前述のとおり、トリガーは使用しません。より効率的な方法は、 MERGEを使用することです。上記と同じセットアップを使用します。
SQL> merge into tmp_test o
2 using ( select a.id
3 from ( select level as id
4 from dual
5 connect by level <= 3 ) a
6 left outer join tmp_blocked b
7 on a.id = b.id
8 where b.id is null
9 ) n
10 on ( o.id = n.id )
11 when not matched then
12 insert values (n.id);
2 rows merged.
SQL>
SQL> select * from tmp_test;
ID
----------
1
2
さらに簡単な方法は、MINUSを使用することです。
insert into tmp_test
select level
from dual
connect by level <= 3
minus
select id
from tmp_banned