web-dev-qa-db-ja.com

トリガーで変更テーブルエラーが発生しないのはなぜですか?

トリガー内の変更テーブルではDMLステートメントを使用できないことがわかっています(または少なくとも知っていました)。 Oracleドキュメント からの抜粋:

変更テーブルは、UPDATE、DELETE、またはINSERTステートメントによって変更されるテーブル、またはDELETE CASCADE制約の影響によって更新される可能性があるテーブルです。

トリガーステートメントを発行したセッションは、変更テーブルをクエリまたは変更できません。この制限により、トリガーが一貫性のないデータセットを参照するのを防ぎます。

ただし、SQL DeveloperまたはSQL * Plusを使用してinsert into empを実行したときに、このデモトリガーが「変更テーブル」エラーで失敗しない理由を理解できません。

CREATE OR REPLACE TRIGGER emp_bri   
  BEFORE INSERT ON emp 
    FOR EACH ROW
BEGIN

  SELECT max(id) + 1 INTO :NEW.id FROM emp;
  UPDATE emp SET salary = 5000;

END emp_bri;

挿入は次のid値で正常に完了し、すべてのempレコードを更新します。 Oracle Database 11g Enterprise Editionリリース11.2.0.1.0を使用しています。複合トリガーについて読みましたが、サンプルではそれらを使用していません。

11
Centurion

例外があります。テーブルにbefore insertの行レベルのトリガーを定義し、単一の行INSERTステートメントを発行しても、table is mutatingエラーは発生しません。ただし、同じ種類のトリガーを定義して複数行のINSERTステートメントを発行すると、エラーが発生します。次に例を示します。

SQL> create table TB_TR_TEST(
  2    col1 number,
  3    col2 number
  4  )
  5  ;

Table created

SQL> create or replace trigger TR_TB_TR_TEST
  2  before insert on TB_TR_TEST
  3  for each row
  4  begin
  5    SELECT max(col1) + 1 INTO :NEW.col1
  6      FROM TB_TR_TEST;
  7    UPDATE TB_TR_TEST SET col2 = 5000;
  8  end;
  9  /

Trigger created

次に、単一行のinsertステートメントを示します。これにより、変換テーブルエラーは発生しません。

SQL> insert into TB_TR_TEST(col1, col2) values(1,2);

1 row inserted

SQL> insert into TB_TR_TEST(col1, col2) values(3,5);

1 row inserted

SQL> commit;

Commit complete

次に、複数行の挿入ステートメントを示します。これにより、変更テーブルエラーが発生します。

SQL> insert into TB_TR_TEST(col1, col2)
  2    select 1, 2
  3      from dual;

insert into TB_TR_TEST(col1, col2)
  select 1, 2
    from dual

ORA-04091: table HR.TB_TR_TEST is mutating, trigger/function may not see it
ORA-06512: at "HR.TR_TB_TR_TEST", line 2
ORA-04088: error during execution of trigger 'HR.TR_TB_TR_TEST'
12