2017-08-17 2 views
0

Actuellement, j'implémente une procédure qui crée quelques lignes dans certaines tables liées à partir d'un modèle. Donc, ma procédure consiste en un SAVEPOINT suivi de quelques instructions INSERT sur différentes tables, et un curseur pour insérer d'autres lignes dans d'autres tables tout en faisant référence aux clés primaires nouvellement créées.Transaction atomique avec des déclencheurs BEFORE INSERT/UPDATE imbriqués

Chacune de ces tables a un déclencheur BEFORE INSERT/UPDATE défini qui a pour but de:

  • Obtenir une nouvelle clé primaire à partir d'un séquenceur si elle est pas définie dans l'instruction INSERT (il y a des cas où Je dois régler le plus tard sur la touche primaire pour faire référence explicitement dans la même transaction)
  • définir des valeurs par défaut si elles sont NULL
  • définir les champs d'audit (last_change_date, last_change_user, etc ..)

La transaction échoue avec ORA-04091: table mute, déclenchement/fonction peut ne pas voir

Je comprends, que je pouvais résoudre ce, en déclarant TRANSACTION PRAGMA AUTONOME dans chaque déclenchement, mais mon opération ne plus être atomique, car c'est l'exigence que tous ces ensembles de données soient créés/insérés dans leur ensemble ou aucun d'entre eux.

Alors qu'est-ce que je fais de mal dans la conception de ma base de données?


MISE À JOUR: Voici le code du déclencheur

CREATE TRIGGER TRG_AUFTRAG_B_IU 
    BEFORE INSERT OR UPDATE 
    ON AUFTRAG 
    FOR EACH ROW 
    BEGIN 
    IF INSERTING THEN 
    IF :new.id is NULL or :new.id = 0 THEN 
     SELECT SEQ_AUFTRAG.nextval into :new.id from dual; 
    END IF; 

    IF :new.nummer is NULL or :new.nummer = 0 THEN 
     SELECT nvl(MAX(NUMMER),0)+1 INTO :new.nummer FROM AUFTRAG WHERE EXTRACT(YEAR from DATUM) = EXTRACT(YEAR from :new.DATUM); 
    END IF; 

    --DEFAULT Values 
    IF :new.BETR_GRENZWERTE_RELEVANT is NULL THEN 
     SELECT 0 INTO :new.BETR_GRENZWERTE_RELEVANT FROM dual; 
    END IF; 

    IF :new.DOKUMENTE_ABGELEGT is NULL THEN 
     SELECT 0 INTO :new.DOKUMENTE_ABGELEGT FROM dual; 
    END IF; 

    IF :new.EXT_ORG is NULL or :new.EXT_ORG < 1 THEN 
     SELECT 1 INTO :new.EXT_ORG FROM dual; 
    END IF; 

    :new.ERSTELLT_VON := nvl(:new.ERSTELLT_VON,user); 
    :new.ERSTELLT_DATUM := nvl(:new.ERSTELLT_DATUM,sysdate); 
    END IF; 

    :new.GEAENDERT_VON := user; 
    :new.GEAENDERT_DATUM := sysdate; 
    END; 
+2

Veuillez nous montrer le code de votre déclencheur. En fait, les actions que vous énumérez sont très typiques et utiles pour les déclencheurs, elles devraient fonctionner sans aucun problème. –

+2

Il semble que vous exécutiez 'select' à partir de la même table dans le déclencheur. Sur la base de ce que vous dites que le déclencheur doit faire, il n'y a aucune exigence pour «sélectionner». Et vous avez raison - 'pragma autonome transaction 'n'est pas une solution à ce genre de problème. –

+1

ah! Je pense que je vois déjà quel est le problème ... c'est le: nouveau: NUMMER part n'est-ce pas? Ceci est pour créer un numéro d'identification qui est une exigence de l'entreprise. Serait-ce une solution de le faire dans un AFTER INSERT TRIGGER? –

Répondre

1

Vous pouvez l'écrire plus compact comme ceci:

CREATE TRIGGER TRG_AUFTRAG_B_IU 
    BEFORE INSERT OR UPDATE 
    ON AUFTRAG 
    FOR EACH ROW 
    BEGIN 
    IF INSERTING THEN 
     :new.id = NVL(NULLIF(:new.id, 0), SEQ_AUFTRAG.nextval); 

     --DEFAULT Values 
     :new.BETR_GRENZWERTE_RELEVANT := NVL(:new.BETR_GRENZWERTE_RELEVANT, 0); 
     :new.DOKUMENTE_ABGELEGT := NVL(:new.DOKUMENTE_ABGELEGT, 0); 

     IF :new.EXT_ORG is NULL or :new.EXT_ORG < 1 THEN 
      :new.EXT_ORG := 1; 
     END IF;  
     :new.ERSTELLT_VON := nvl(:new.ERSTELLT_VON,user); 
     :new.ERSTELLT_DATUM := nvl(:new.ERSTELLT_DATUM,sysdate); 
    END IF; 

    :new.GEAENDERT_VON := user; 
    :new.GEAENDERT_DATUM := sysdate; 
END; 

Seul "problème" est cette partie

IF :new.nummer is NULL or :new.nummer = 0 THEN 
    SELECT nvl(MAX(NUMMER),0)+1 INTO :new.nummer 
    FROM AUFTRAG 
    WHERE EXTRACT(YEAR from DATUM) = EXTRACT(YEAR from :new.DATUM); 
END IF; 

Celui-ci vous devriez mettre dans votre procédure ou dans un déclencheur de déclaration (c.-à-d. sans FOR EACH ROW clause) comme ceci:

CREATE TRIGGER TRG_AUFTRAG_B_A 
    AFTER INSERT ON AUFTRAG 
BEGIN 
    UPDATE 
     (SELECT ID, NUMMER, 
      ROW_NUMBER() OVER (PARTITION BY EXTRACT(YEAR from DATUM) ORDER BY ID) as N 
     FROM AUFTRAG) 
    SET NUMMER = N 
    WHERE NUMMER IS NULL;   
END; 
+0

Merci beaucoup! Non seulement pour rendre ces concepts - tableaux mutants - clairs pour moi, mais aussi pour suggérer des améliorations de code. –