1

私には2つのテーブルがXXありYY、更新の場合にそれらのトリガーが互いに呼び出しています。

XX のトリガーは次のようになります。

CREATE OR REPLACE TRIGGER SCMA.XX_RBIU
 BEFORE INSERT OR UPDATE
 ON SCMA.XX  FOR EACH ROW

-- PL/SQL BLOCK
BEGIN
    IF UPDATING THEN
            -- Only update the YY row if the branch id has 
            -- been modified on the XX row
            IF :NEW.BRANCH_ID <> :OLD.BRANCH_ID THEN 
                UPDATE YY TP
                SET TP.BRANCH_ID = :NEW.BRANCH_ID
                WHERE TP.XX_ID = :NEW.XX_ID;
            END IF;
    END IF;

    ...
    ... -- Other PL/SQL statements that do some necessary
    ... -- computation and do not use any SQL.
    ...
END;
/

YY のトリガーは次のようになります。

CREATE OR REPLACE TRIGGER SCMA.YY_RBIU
BEFORE INSERT OR UPDATE
ON SCMA.YY
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE

v_xx_type     xx.xx_type_code%TYPE;

BEGIN

 select x.xx_type_code
    into v_xx_type
    from XX x
    where x.xx_id = :new.xx_id;

    ...
    ... -- Other PL/SQL statements that do some necessary
    ... -- computation and do not use any SQL.
    ...
END;
/

SELECTトリガーのステートメントでYY_RBIUこのエラーが発生していることはわかっています。それを回避するためにトリガーをコーディングするにはどうすればよいですか?

SELECTステートメントをブロックYY_RBIU内にラップしようとしましIF INSERTING THENたが、これは更新に対して実行されません。SELECTupdate が trigger から呼び出されている場合、このステートメントをスキップするにはどうすればよいXX_RBIUですか?

私も入れてみPRAGMA AUTONOMOUS_TRANSACTIONましXX_RBIUたが、デッドロックになっています。

thisthisthisthisも参照してみましたが、解決できませんでした。

どんな助けでも大歓迎です。

4

3 に答える 3

1

私がトリガーを避けるいくつかの理由の 1 つです。基本的に、循環トリガーの問題がない、より適切なソリューションを考え出す必要があります。次のものを追加することもできます。

IF :NEW.BRANCH_ID <> :OLD.BRANCH_ID

YYトリガーにも。ただし、これは、トリガーが本物の更新を見逃していることを意味する場合があります。

うまくいくハッキーな解決策は、新しいYY_flagテーブルを持つことです:

YY_FLAG
xx_id    (Primary Key)

次に、XXトリガーで:

INSERT INTO yy_flag VALUES( :new.xx_id );
UPDATE YY ...
DELETE FROM yy_flag WHERE xx_id = :new.xx_id;

そしてあなたのYYトリガーで:

BEGIN
    SELECT count(1) INTO is_trigger FROM yy_flag WHERE  xx_id = :new.xx_id;
    IF is_trigger = 0 THEN
         SELECT FROM XX
         ...

したがって、基本的にテーブルには、トリガーを実行してyy_flagいる特定のレコードのみが含まれます。xx_idそして、目的は行をテーブルに決してコミットしないことでありyy_flag、オラクルの通常のロックはすべての同時実行性を処理する必要があります。

私が言ったように、これは非常にハックですが、何らかの理由でソリューションを再設計できない場合は機能するはずです。

于 2013-11-12T04:01:56.487 に答える
0

私にとっては、複合トリガーが機能しました。この複合トリガーでは、UPDATEステートメントをセグメントから削除しBEFORE EACH ROWてセグメントに配置するAFTER STATEMENTと、機能しました。必要な値を保持するためにコレクションを使用しました。

トリガーは次のとおりです。

CREATE OR REPLACE TRIGGER SCMA.TRANSACTION_COMPOUND
  FOR INSERT OR UPDATE ON SCMA.XX
    COMPOUND TRIGGER

    -- DECLARE GLOBAL VARIABLES THAT WOULD BE USED ACROSS
    -- THE DIFFERENT EVENTS.
    TYPE rec_chg_br IS RECORD (
        xx_id    XX.xx_id%TYPE,
        branch_id        jbs_branch.branch_id%TYPE
    );
    TYPE t_chg_br IS TABLE OF rec_chg_br
        INDEX BY PLS_INTEGER;
    chg_branch t_chg_br;

  BEFORE EACH ROW IS
  BEGIN
    IF UPDATING THEN
            -- Only update the YY row if the branch id has 
            -- been modified on the XX row
            IF :NEW.BRANCH_ID <> :OLD.BRANCH_ID THEN
                chg_branch(:new.xx_id).xx_id := :new.xx_id;
                chg_branch(:new.xx_id).currency_id := :new.currency_id;
                chg_branch(:new.xx_id).branch_id := :new.branch_id;
            END IF;
    END IF;
    ...
    ... -- Other PL/SQL statements that do some necessary
    ... -- computation and do not use any SQL.
    ...
  END BEFORE EACH ROW;

  AFTER STATEMENT IS
  BEGIN
    -- NULL; -- Do something here.
    FORALL i IN chg_branch.FIRST..chg_branch.LAST
        UPDATE YY
        SET BRANCH_ID = chg_branch(i).branch_id,
               CURRENCY_ID = chg_branch(i).currency_id
        WHERE xx_id = chg_branch(i).xx_id;
  END AFTER STATEMENT;

END TRANSACTION_COMPOUND;
/

trigger に変更は必要ありませんでしたYY_RBIU

于 2013-11-12T06:26:22.427 に答える