1

问题是这样的:我在名为 CLAN_AFFILIATI 的表上实现了一个触发器,它增加(如果 inseriemento)并减少(如果取消)另一个名为 CLAN 的表的属性 (NUMAFFILIATI)。我要做的是阻止用户对 Clan 的更新 NUMAFFILIATI 并考虑在 CLAN 上再触发一次:

触发 CLAN_AFFILIATI(CLAN VARCHAR,AFFILIATO VARCHAR,RUOLO VARCHAR)

CREATE OR REPLACE TRIGGER  "AggiornamentoNumAffiliati" 
AFTER INSERT OR DELETE ON CLAN_AFFILIATI
FOR EACH ROW
DECLARE
  CLAN_APPARTENENZA VARCHAR(20);
BEGIN


  IF INSERTING THEN

    SELECT NOME INTO CLAN_APPARTENENZA
    FROM CLAN
    WHERE NOME=:new.CLAN;

    UPDATE CLAN 
    SET NUMAFFILIATI=NUMAFFILIATI+1
    WHERE CLAN_APPARTENENZA=NOME;
  ELSE
    SELECT NOME INTO CLAN_APPARTENENZA
    FROM CLAN
    WHERE NOME=:old.CLAN;

    UPDATE CLAN 
    SET NUMAFFILIATI=NUMAFFILIATI-1
    WHERE CLAN_APPARTENENZA=NOME;
  END IF; 
END;

触发 CLAN (NAME VARCHAR ,NUMAFFILIATI INTEGER)

CREATE OR REPLACE TRIGGER  "ModificaNumAffiliati" 
BEFORE INSERT OR UPDATE OF NUMAFFILIATI ON CLAN
FOR EACH ROW
DECLARE
CONT NUMBER:=0;  
BEGIN

   IF INSERTING THEN
      IF :new.NUMAFFILIATI <> 0 THEN
           RAISE_APPLICATION_ERROR(-20016,'NUMERO ERRATO');
      END IF;
   ELSE
      SELECT COUNT(*) INTO CONT
      FROM CLAN_AFFILIATI
      WHERE :old.NOME=CLAN;
      IF CONT <> :new.NUMAFFILIATI THEN
          RAISE_APPLICATION_ERROR(-20017,'NUMERO ERRATO');
      END IF;
   END IF;
 END;

但我正在做的是报告一个错误:

error ORA-04091: Table ANTONIO.CLAN_AFFILIATI is being modified, the trigger / function can not read 
ORA-06512: at "ANTONIO.ModificaNumAffiliati", line 10 
ORA-04088: error during execution of trigger 'ANTONIO.ModificaNumAffiliati' 
ORA-06512: at "ANTONIO.AggiornamentoNumAffiliati", line 12 
ORA-04088: error during execution of trigger 'ANTONIO.AggiornamentoNumAffiliati

我怎么解决这个问题 ....

4

4 回答 4

2

这可能是解决方案:

我用这个示例表对其进行了测试:

CREATE TABLE CLAN_AFFILIATI(CLAN VARCHAR2(100),AFFILIATO VARCHAR2(100),RUOLO VARCHAR2(100));
CREATE TABLE CLAN (NOME VARCHAR2(100) ,NUMAFFILIATI NUMBER(10));

你需要这个帮助程序包。

CREATE OR REPLACE PACKAGE STORE_NOMES
AS

    TYPE record_nomes IS RECORD (
        nome VARCHAR2(100),
        operation VARCHAR2(100) -- insert or delete
    );

    TYPE array_type_nomes IS TABLE OF record_nomes INDEX BY BINARY_INTEGER;
    g_array_nomes array_type_nomes;

END STORE_NOMES;
/

在 CLAN 表上触发:

CREATE OR REPLACE TRIGGER MODIFICANUMAFFILIATI
  BEFORE INSERT OR UPDATE OF NUMAFFILIATI ON CLAN
FOR EACH ROW
DECLARE
    l_CONT NUMBER:=0;
BEGIN

   IF INSERTING THEN
      -- prevent inserting <> 0
      IF :new.NUMAFFILIATI <> 0 THEN
           RAISE_APPLICATION_ERROR(-20016,'NUMERO ERRATO');
      END IF;
   ELSE
      SELECT COUNT(*) INTO l_CONT
      FROM CLAN_AFFILIATI
      WHERE CLAN = :old.NOME;
      IF l_CONT <> :new.NUMAFFILIATI THEN
          RAISE_APPLICATION_ERROR(-20017,'NUMERO ERRATO');
      END IF;
   END IF;
 END;
/

在 CLAN_AFFILIATI 表上触发语句之前:

CREATE OR REPLACE TRIGGER TRG_CLAN_AFFILIATI_BEFORE_STMT
  BEFORE INSERT OR DELETE
ON CLAN_AFFILIATI
DECLARE
BEGIN
    STORE_NOMES.g_array_nomes.DELETE;
END;
/

在 CLAN_AFFILIATI 表上触发语句后:

CREATE OR REPLACE TRIGGER TRG_CLAN_AFFILIATI_AFTER_STMT
  AFTER INSERT OR DELETE
ON CLAN_AFFILIATI
DECLARE
BEGIN
    FOR i IN STORE_NOMES.g_array_nomes.FIRST..STORE_NOMES.g_array_nomes.LAST LOOP
        IF(STORE_NOMES.g_array_nomes(i).operation = 'INSERTING') THEN
            UPDATE CLAN
            SET NUMAFFILIATI=NUMAFFILIATI+1
            WHERE NOME = STORE_NOMES.g_array_nomes(i).NOME;
        ELSIF(STORE_NOMES.g_array_nomes(i).operation = 'DELETING') THEN
            UPDATE CLAN
            SET NUMAFFILIATI=NUMAFFILIATI-1
            WHERE NOME = STORE_NOMES.g_array_nomes(i).NOME;
        END IF;
    END LOOP;
END;
/

CLAN_AFFILIATI 表上的行插入/删除触发器:

CREATE OR REPLACE TRIGGER AGGIORNAMENTONUMAFFILIATI
  BEFORE INSERT OR DELETE ON CLAN_AFFILIATI
FOR EACH ROW
DECLARE
  l_CLAN_APPARTENENZA VARCHAR(20);
BEGIN

  IF INSERTING THEN

    SELECT NOME INTO l_CLAN_APPARTENENZA
    FROM CLAN
    WHERE NOME = :new.CLAN;

    STORE_NOMES.g_array_nomes(STORE_NOMES.g_array_nomes.COUNT).nome := :new.CLAN;
    STORE_NOMES.g_array_nomes(STORE_NOMES.g_array_nomes.LAST).operation := 'INSERTING';

  ELSE
    SELECT NOME INTO l_CLAN_APPARTENENZA
    FROM CLAN
    WHERE NOME = :old.CLAN;

    STORE_NOMES.g_array_nomes(STORE_NOMES.g_array_nomes.COUNT).nome := :old.CLAN;
    STORE_NOMES.g_array_nomes(STORE_NOMES.g_array_nomes.LAST).operation := 'DELETING';
  END IF;
END;
/

现在工作(没有ORACLE-EXCEPTION):

INSERT INTO CLAN(NOME, NUMAFFILIATI) VALUES('Antonio', 0);
INSERT INTO CLAN_AFFILIATI(CLAN,AFFILIATO,RUOLO) values('Antonio','Affiliato1','Ruolo1');
INSERT INTO CLAN_AFFILIATI(CLAN,AFFILIATO,RUOLO) values('Antonio','Affiliato2','Ruolo2');
于 2010-08-31T14:40:10.220 回答
0

ORA-04091 也称为“变异表”错误 - 基本上,行触发器无法查询或更改触发器操作的表。

@Martin 的回答是关于如何解决此问题的经典描述,但如果您使用的是 Oracle 11+,您可以使用复合触发器来做同样的事情。

分享和享受。

于 2010-09-02T11:22:41.310 回答
0

更改第一个触发器“AggiornamentoNumAffiliati”,使其不会立即尝试更新战队,而是将名称(NOME)存储在包内的 PL/SQL 表中;然后,您创建一个 AFTER INSERT OR DELETE(但没有 FOR EACH ROW 子句)触发器,该触发器从包中读取 PL/SQL 表并相应地更新 CLAN。

于 2010-08-31T13:35:01.207 回答
0

我没有带我的开发人员工具,但在我看来,好像你让自己陷入了某种循环依赖问题。当您的 CLAN_AFFILIATI 触发器被引发时,您会在其中更新 CLAN,它会调用第二个触发器,该触发器具有从 ELSE 块中的 CLAN_AFFILIATI 表中选择的内容。

也许插入前(第一个查询)和插入后(第二个查询)也有影响。

于 2010-08-31T13:36:24.480 回答