Vor Oracle 11g konnte die Ausführungsreihenfolge gleicher Trigger auf einer Tabelle nicht beeinflusst werden. Dieses kann ab Oracle 11g mit der FOLLOWS-Klausel erreicht werden.

CONN HR/hr
SET ECHO ON
SET SERVEROUTPUT ON
CL SCR

CREATE TABLE TAB_TEST
(
 S1 NUMBER,
 S2 VARCHAR2(10)
);

CREATE OR REPLACE TRIGGER TR_1
BEFORE INSERT ON TAB_TEST
FOR EACH ROW
BEGIN
 DBMS_OUTPUT.PUT_LINE('TR_1');
END;
/

CREATE OR REPLACE TRIGGER TR_2
BEFORE INSERT ON TAB_TEST
FOR EACH ROW
BEGIN
 DBMS_OUTPUT.PUT_LINE('TR_2');
END;
/
PAUSE

SET ECHO OFF
EXEC DBMS_OUTPUT.PUT_LINE('INSERTING DATA');
INSERT INTO TAB_TEST VALUES(1,'TEST');
EXEC DBMS_OUTPUT.PUT_LINE('-----');
INSERT INTO TAB_TEST VALUES(1,'TEST');

PAUSE

DROP TABLE TAB_TEST PURGE;

SET ECHO ON

CREATE TABLE TAB_TEST
(
 S1 NUMBER,
 S2 VARCHAR2(10)
);

CREATE OR REPLACE TRIGGER TR_1
BEFORE INSERT ON TAB_TEST
FOR EACH ROW
BEGIN
 DBMS_OUTPUT.PUT_LINE('TR_1');
END;
/

CREATE OR REPLACE TRIGGER TR_2
BEFORE INSERT ON TAB_TEST
FOR EACH ROW
FOLLOWS TR_1
BEGIN
 DBMS_OUTPUT.PUT_LINE('TR_2');
END;
/

PAUSE

SET ECHO OFF
EXEC DBMS_OUTPUT.PUT_LINE('INSERTING DATA');
INSERT INTO TAB_TEST VALUES(1,'TEST');
EXEC DBMS_OUTPUT.PUT_LINE('-----');
INSERT INTO TAB_TEST VALUES(1,'TEST');

PAUSE

DROP TABLE TAB_TEST PURGE;

SET ECHO ON

Compound Trigger in Oracle 11g zur Vermeidung von mutierenden Triggern.

Das folgende Beispiel zeigt einen mutierenden Trigger. Mutierende Trigger entstehen, wenn ein ROW-Trigger auf die Tabelle Abfragen oder Änderungen durchführt, auf die er gelegt wurde. Die Ursache, dass die Ausführung dieses Triggers fehlschlägt liegt darin begründet, dass er bei DML-Anweisungen, die mehrere Zeilen in der Tabelle ändern, für jede Zeilenänderung ausgeführt wird. Somit greift der Trigger in eine laufende Änderungsaktion ein und erhält somit eine inkonsistente Sicht auf die Daten.


CONN HR/hr

SET ECHO ON
SET SERVEROUTPUT ON
CL SCR

SET ECHO ON

CREATE OR REPLACE TRIGGER TR_SAL_CHECK
BEFORE INSERT OR UPDATE
ON EMPLOYEES
FOR EACH ROW
DECLARE
 HIGH_SAL EMPLOYEES.SALARY%TYPE;
 LOW_SAL EMPLOYEES.SALARY%TYPE;
BEGIN
 SELECT MAX(SALARY),MIN(SALARY)
 INTO HIGH_SAL, LOW_SAL
 FROM EMPLOYEES;
 IF :NEW.SALARY NOT BETWEEN LOW_SAL AND HIGH_SAL
 THEN
  RAISE_APPLICATION_ERROR(-20000,'GEHALT ZU HOCH ODER ZU NIEDRIG');
 END IF;
END;
/

PAUSE

UPDATE EMPLOYEES SET SALARY=SALARY*1.1 WHERE EMPLOYEE_ID=100;

UPDATE EMPLOYEES SET SALARY=SALARY*1.1 WHERE EMPLOYEE_ID=100       
* ERROR at line 1: ORA-04091: table HR.EMPLOYEES is mutating, trigger/function may not see it
ORA-06512: at "HR.TR_SAL_CHECK", line 5
ORA-04088: error during execution of trigger 'HR.TR_SAL_CHECK'

PAUSE

DROP TRIGGER TR_SAL_CHECK;

Um dieses Problem zu lösen, wurde der Compound-Trigger eingeführt, der eine Zusammensetzung aller DML-Triggerarten ermöglicht. Hierbei kann ein Compound-Trigger eine Zusammensetzung aus Before-Statemant, Before-Row, After-Row und After-Statement sein.

Die Ausführungsreihenfolge der Triggerteile wäre dann:

  1. Einmalig der Before-Statement Teil
  2. Für jede Zeile erst der Before-Row und dann der After-Row Teil
  3. Einmalig der After-Statement Teil

Da ein Compound-Trigger einen globalen Deklarationsbereich besitzt, können hier Variablen definiert werden, die von allen Triggerteilen verwendet werden können. Da ein mutierender Trigger nicht bei Statement-Triggern auftaucht, weil er entweder vor oder am Ende der Anweisung ausgeführt wird, können diese Teile verwendet werden, um globale Variablen zu füllen, deren Werte dann an den Row-Trigger-Bereich weitergeleitet werden.

Die Lösung des im ersten Beispiel entstandenen Problems liegt in der Verwendung eines Compound Triggers:

CONN HR/hr
SET ECHO ON
SET SERVEROUTPUT ON
CL SCR

SET ECHO ON

CREATE OR REPLACE TRIGGER TR_SAL_CHECK
FOR INSERT OR UPDATE
ON EMPLOYEES
COMPOUND TRIGGER
  -- Global declaration.
  HIGH_SAL EMPLOYEES.SALARY%TYPE;
  LOW_SAL EMPLOYEES.SALARY%TYPE;

  BEFORE STATEMENT IS
  BEGIN
    SELECT MAX(SALARY),MIN(SALARY)
 INTO HIGH_SAL, LOW_SAL
 FROM EMPLOYEES;
  END BEFORE STATEMENT;

  BEFORE EACH ROW IS
  BEGIN
    IF :NEW.SALARY NOT BETWEEN LOW_SAL AND HIGH_SAL
 THEN
  RAISE_APPLICATION_ERROR(-20000,'GEHALT ZU HOCH ODER ZU NIEDRIG');
 END IF;
  END BEFORE EACH ROW;
END TR_SAL_CHECK;
/

PAUSE

UPDATE EMPLOYEES SET SALARY=SALARY*1.1 WHERE EMPLOYEE_ID=100;

UPDATE EMPLOYEES SET SALARY=SALARY*1.1 WHERE EMPLOYEE_ID=100       
* ERROR at line 1: ORA-20000: GEHALT ZU HOCH ODER ZU NIEDRIG
ORA-06512: at "HR.TR_SAL_CHECK", line 17
ORA-04088: error during execution of trigger 'HR.TR_SAL_CHECK'

PAUSE

DROP TRIGGER TR_SAL_CHECK;