あなたが遭遇したのは、古典的な「ミューティングテーブル」の例外です。 ROWトリガーでは、Oracleでは、トリガーが定義されているテーブルに対してクエリを実行することはできません。したがって、これはSELECT
です。 DELETING
のTABLE1に対して この問題を引き起こしているトリガーの一部。
これを回避するには、いくつかの方法があります。おそらく、この状況で最適なのは、次のような複合トリガーを使用することです。
CREATE OR REPLACE TRIGGER TABLE1_NUM_TRG
FOR INSERT OR DELETE ON TABLE1
COMPOUND TRIGGER
TYPE NUMBER_TABLE IS TABLE OF NUMBER;
tblTABLE2_IDS NUMBER_TABLE;
BEFORE STATEMENT IS
BEGIN
tblTABLE2_IDS := NUMBER_TABLE();
END BEFORE STATEMENT;
AFTER EACH ROW IS
BEGIN
IF INSERTING THEN
UPDATE TABLE2 t2
SET t2.TABLE2NUM = :new.NUM
WHERE t2.ID = :new.TABLE2_ID;
ELSIF DELETING THEN
tblTABLE2_IDS.EXTEND;
tblTABLE2_IDS(tblTABLE2_IDS.LAST) := :new.TABLE2_ID;
END IF;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
IF tblTABLE2_IDS.COUNT > 0 THEN
FOR i IN tblTABLE2_IDS.FIRST..tblTABLE2_IDS.LAST LOOP
UPDATE TABLE2 t2
SET t2.TABLE2NUM = (SELECT NUM
FROM (SELECT t1.NUM
FROM TABLE1 t1
WHERE t1.TABLE2_ID = tblTABLE2_IDS(i)
ORDER BY modification_date DESC)
WHERE ROWNUM = 1)
WHERE t2.ID = tblTABLE2_IDS(i);
END LOOP;
END IF;
END AFTER STATEMENT;
END TABLE1_NUM_TRG;
複合トリガーにより、各タイミングポイント(BEFORE STATEMENT
、BEFORE ROW
、AFTER ROW
、およびAFTER STATEMENT
)処理されます。タイミングポイントは常に指定された順序で呼び出されることに注意してください。適切なSQLステートメント(つまり、INSERT INTO TABLE1
またはDELETE FROM TABLE1
)が実行され、このトリガーが起動されます。呼び出される最初のタイミングポイントはBEFORE STATEMENT
になります。 、およびBEFORE STATEMENT
のコード ハンドラは、多数の数値を保持するためにPL/SQL表を割り当てます。この場合、PL / SQL表に格納される数値は、TABLE1のTABLE2_ID値になります。 (たとえば、テーブルはさまざまな数の値を保持できるため、配列の代わりにPL / SQLテーブルが使用されますが、配列を使用する場合は、格納する必要のある数値を事前に知っておく必要があります。特定のステートメントの影響を受ける行数を事前に知ることはできないため、PL /SQL表を使用します。
AFTER EACH ROW
の場合 タイミングポイントに到達し、処理中のステートメントがINSERTであることがわかりました。トリガーは先に進み、問題が発生しないため、TABLE2に対して必要なUPDATEを実行します。ただし、DELETEが実行されている場合、トリガーはTABLE1.TABLE2_IDを前に割り当てられたPL/SQL表に保存します。 AFTER STATEMENT
最終的にタイミングポイントに到達し、前に割り当てられたPL / SQL表が繰り返され、TABLE2_IDが見つかるたびに、適切な更新が実行されます。
ドキュメントはこちら。