web-dev-qa-db-fra.com

Je ne sais pas comment réparer l'erreur de déclenchement mutation d'ora-04091

J'apprends Oracle SQL, mais lorsque vous faites face à des déclencheurs, je trouve une erreur, cela m'empêche de mettre à jour les valeurs de cette table.

L'erreur est:

erreur ORA-04091: TABLE USUARIO.LINEAS MUTATIF.LINEASE MUTATIVE, la fonction/fonction peut ne pas la voir ORA-06512: à "usuario.trg_actualizarpedido", ligne 5 ora-04088: Erreur lors de l'exécution de la gâchette 'usuario.trg_actualizarpedido'

J'apprends à ce sujet, mais je ne trouve pas de solution ou de comprendre par moi-même quel est le problème.

CREATE OR REPLACE TRIGGER trg_actualizarpedido
AFTER INSERT OR UPDATE OR DELETE ON LINEAS
FOR EACH ROW

DECLARE
    sumaImporteLineas NUMBER := 0;

BEGIN
    SELECT SUM(IMPORTE) INTO sumaImporteLineas FROM LINEAS WHERE NUMPEDIDO = :NEW.NUMPEDIDO;
    UPDATE PEDIDOS ped SET ped.TOTAL = sumaImporteLineas WHERE ped.NUM = :NEW.NUMPEDIDO;
END;

Merci!

3
Daniel Hernández

Ora-04091

C'est la manière d'Oracle de dire:

vous ne voulez vraiment pas faire ça.

Voici ma courte-liste des solutions de contournement.

Option 1

Utilisez un VIEW.

C'est la méthode préférée.

Option 2

Utiliser un Materialized View.

Oui, vous pouvez rafraîchir la SUM () sur COMMIS.

Option 3

Utilisez un TAPI (API de table). (A Package)

Toutes les opérations DML doivent parcourir ce Package; jamais contre la table réelle.

Option 4

Solution à 3 déclencheurs. (Cela pourrait être un déclencheur unique complexe.)

Sans verrouillage approprié, vous pourriez avoir des "mises à jour perdues".

Même avec un verrouillage correct, des serrures mortes pourraient se produire.

1
Michael Kutz

Dans une gâchette réagissant à un changement à une table, vous ne pouvez pas interroger la table elle-même. Mais vous n'avez pas besoin de.

Il semble que vous essayiez de conserver un total de fonctionnement en fonction des modifications apportées à la valeur de l'importante. Pour cela, vous n'avez pas besoin d'un mais trois déclencheurs:

/* Insert - Add New value */ 
CREATE OR REPLACE TRIGGER trg_actualizarpedido1
AFTER INSERT ON LINEAS
FOR EACH ROW
BEGIN
    UPDATE PEDIDOS 
    SET TOTAL = TOTAL + :NEW.IMPORTE 
    WHERE NUMPEDIDO = :NEW.NUMPEDIDO ; 
END;

/* Update - Subtract the Old value, Add New value */ 
CREATE OR REPLACE TRIGGER trg_actualizarpedido2
AFTER UPDATE ON LINEAS
FOR EACH ROW
BEGIN
    UPDATE PEDIDOS 
    SET TOTAL = TOTAL - :OLD.IMPORTE + :NEW.IMPORTE 
    WHERE NUMPEDIDO = :NEW.NUMPEDIDO ; 
END;

/* Delete - Subtract the Old value */ 
CREATE OR REPLACE TRIGGER trg_actualizarpedido3
AFTER DELETE ON LINEAS
FOR EACH ROW
BEGIN
    UPDATE PEDIDOS 
    SET TOTAL = TOTAL - :OLD.IMPORTE 
    WHERE NUMPEDIDO = :OLD.NUMPEDIDO ; 
END;
0
Phill W.

Comme déjà donné dans les autres réponses en utilisant un déclencheur pour une telle tâche n'est pas la manière préférée de le faire. Par souci d'exhaustivité, une solution basée sur la gâchette (non testée):

create or replace trigger trg_actualizarpedido
  for insert or update or delete on LINEAS 
  COMPOUND TRIGGER

  type NUMPEDIDO_t is table of LINEAS.NUMPEDIDO%type;
  NUMPEDIDOs NUMPEDIDO_t ;
  sumaImporteLineas NUMBER;

  before statement is 
  begin
    NUMPEDIDOs := NUMPEDIDO_t();
  end before statement;

  after each row is 
  begin
    NUMPEDIDOs.EXTEND;
    IF INSERTING OR UPDATING ('NUMPEDIDO') THEN
       NUMPEDIDOs(NUMPEDIDOs.LAST) := :NEW.NUMPEDIDO;
    ELSIF DELETING THEN
       NUMPEDIDOs(NUMPEDIDOs.LAST) := :OLD.NUMPEDIDO;
    END IF;
  end after each row;

  after statement is 
  begin      
    for i in NUMPEDIDOs.FIRST..NUMPEDIDOs.LAST LOOP
       SELECT SUM(IMPORTE) INTO sumaImporteLineas 
       FROM LINEAS 
       WHERE NUMPEDIDO = NUMPEDIDOs(i);

       UPDATE PEDIDOS ped SET ped.TOTAL = sumaImporteLineas 
       WHERE ped.NUM = NUMPEDIDOs(i);
    end loop;          
  end after statement;

end;
0