これは、手順で開始時刻と終了時刻を表示するなど、かなり簡単にテストできます。(デモンストレーションするサンプル コードを以下に示します)。セッション A 内で、次の場合:
update <table> set ...;
update <table> set ...;
...トリガーは、そのステートメントの一部としての最初の更新に対して、影響を受ける行ごとに順番に起動します。その後、2 番目の更新が実行され、影響を受ける行ごとにトリガーが再び順番に起動されます。プロシージャは常に 1 回だけ実行されますが、更新を行う可能性があり、それぞれの影響を受ける行が多くなります。
ステートメント レベルのトリガーに変更しても、プロシージャは常に 1 回しか実行されませんが、更新が複数の行に影響する場合は、更新された行ごとに 1 回ではなく、更新ごとに 1 回、全体として実行される回数が少なくなります。
ただし、セッション B から 2 番目の更新を同時に実行した場合 (そしてテーブル内の異なる行に触れている場合)、最初のセッションの影響を受けないため、プロシージャが同時に 2 回実行されます。それが行レベルのトリガーかステートメント レベルのトリガーかは問題ではありません。各セッションで 1 回実行されます。
プロシージャが異なるセッションから 2 回実行されるのを避けたい場合は、プロシージャの開始時に別のテーブルの制御フラグを更新するなど、ある種のロック メカニズムを実装する必要があります。そのセッションがコミットまたはロールバックすると解放され、他のセッションは続行して独自のロックを取得します。
ただし、それほど時間がかかる手順を呼び出すトリガーを持つことは、非常に間違っているようです。ビジネスとアプリケーションのロジックが間違った場所にあるようです...
セッション A で 2 回実行するためのデモ コード:
create table t42 (id number);
insert into t42 values (1);
insert into t42 values (2);
create package p42 as
procedure busy;
end p42;
/
create package body p42 as
procedure busy is
x number;
begin
dbms_output.put_line('Started ' || systimestamp);
for i in 1..200000 loop -- takes about 4s on my system
select 1 into x from dual;
end loop;
dbms_output.put_line('Finished ' || systimestamp);
end busy;
end p42;
/
create trigger trig42
after update on t42
for each row
begin
p42.busy;
end;
/
次に、2 つの更新を実行します。
update t42 set id = id + 1;
update t42 set id = id + 1;
出力を取得します:
2 rows updated.
Started 08-AUG-13 18.17.49.184770000 +01:00
Finished 08-AUG-13 18.17.53.041916000 +01:00
Started 08-AUG-13 18.17.53.042109000 +01:00
Finished 08-AUG-13 18.17.56.841698000 +01:00
2 rows updated.
Started 08-AUG-13 18.17.57.027777000 +01:00
Finished 08-AUG-13 18.18.01.172613000 +01:00
Started 08-AUG-13 18.18.01.172730000 +01:00
Finished 08-AUG-13 18.18.04.963734000 +01:00
プロシージャは合計 4 回実行され、タイムスタンプはプロシージャの実行がシリアルであることを示しています。セッション A とセッション B で ID 固有の更新を同時に実行すると、セッション A には次のように表示されます。
update t42 set id = id + 1 where id = 1;
1 rows updated.
Started 08-AUG-13 18.21.09.098922000 +01:00
Finished 08-AUG-13 18.21.16.355744000 +01:00
セッション B には次のように表示されます。
update t42 set id = id + 1 where id = 2;
Started 08-AUG-13 18.21.09.500643000 +01:00
Finished 08-AUG-13 18.21.16.204506000 +01:00
1 row updated.
ご覧のとおり、タイムスタンプが重複しているため、プロシージャは同時に 2 回実行されています。
非常に単純なロック メカニズムを追加します。
create table t43 (id number);
insert into t43 values(null);
create package body p42 as
procedure busy is
x number;
begin
update t43 set id = 1;
dbms_output.put_line('Started ' || systimestamp);
...
end p42;
次に、セッション A で:
update t42 set id = id + 1 where id = 1;
1 rows updated.
Started 08-AUG-13 18.22.35.058741000 +01:00
Finished 08-AUG-13 18.22.39.288557000 +01:00
rollback;
同時にセッション B:
update t42 set id = id + 1 where id = 2;
Started 08-AUG-13 18.22.40.385602000 +01:00
Finished 08-AUG-13 18.22.43.995601000 +01:00
1 row updated.
rollback;
これで、2 つのセッションからの呼び出しもシリアライズされます。セッション B の更新のプロシージャ コールは、セッション A がロールバックされるまで開始できないため、セッション A が複数のアクションを実行した場合、より長くブロックされる可能性があります。