Hi Jones,
wenn du bei einem BEFORE … FOR EACH ROW-Trigger die Tabelle,
für die der Trigger definiert ist, manipulieren willst, dann
geht das natürlich nicht: Stell dir vor, du machst ein UPDATE
mit einer WHERE-clause, die das Attribut beinhaltet, das du im
Trigger manipulierst. Das Ergebnis wäre dann gfs. davon
abhängig, in welcher Reihenfolge Oracle die Tupel beim Update
behandelt. So eine Situation nennt man „mutating table“.
Zweitens: Wenn du z.B. in einem INSERT-Trigger einen INSERT
machen willst, geht das auch nicht, da dann der Trigger rekursiv
feuert. Typische Fehlermeldung: „Maximale Anzahl offener Cursor
überschritten“
Wenn, wie in deinem Fall, ein UPDATE ein INSERT nach sich ziehen
soll, dann geht das mit folgendem Trick:
Der BEFORE UPDATE … FOR EACH ROW-Trigger sammelt in einer
PL/SQL-Table alle Daten auf, die anschließend mit einem
AFTER UPDATE … -Trigger in die Relation eingetragen werden.
Um eine PL/SQL-Tabelle zu haben, muss man ein PACKAGE anlegen.
Beispiel: Bei jedem UPDATE: Wenn subid = 0, dann wird ein
zusätzlicher Datensatz angelegt, jedoch mit subid + 1.
DROP TABLE test;
CREATE TABLE test (
id INTEGER,
subid INTEGER,
data INTEGER
);
CREATE OR REPLACE PACKAGE test\_data AS
TYPE type\_test IS TABLE OF test%ROWTYPE INDEX BY BINARY\_INTEGER;
table\_test type\_test;
v\_NumEntries BINARY\_INTEGER := 0;
END test\_data;
/
CREATE OR REPLACE TRIGGER test\_bur
BEFORE UPDATE ON test FOR EACH ROW
BEGIN
IF :new.subid = 0 THEN
-- wir merken uns im package: :new.id, :new.subid + 1, :new.data
test\_data.v\_NumEntries := test\_data.v\_NumEntries + 1;
test\_data.table\_test(test\_data.v\_NumEntries).id := :new.id;
test\_data.table\_test(test\_data.v\_NumEntries).subid := :new.subid + 1;
test\_data.table\_test(test\_data.v\_NumEntries).data := :new.data;
END IF;
END;
/
CREATE OR REPLACE TRIGGER test\_au
AFTER UPDATE ON test
BEGIN
-- jetzt iterieren wir über die tables und machen INSERT INTO test...
FOR v\_loop IN 1 .. test\_data.v\_NumEntries LOOP
INSERT INTO test VALUES (
test\_data.table\_test(v\_loop).id,
test\_data.table\_test(v\_loop).subid,
test\_data.table\_test(v\_loop).data);
END LOOP;
-- loop counter wieder zurücksetzen
test\_data.v\_NumEntries := 0;
END;
/
INSERT INTO test VALUES (1,2,3);
UPDATE test SET subid = 0;
SELECT \* FROM test;
ID SUBID DATA
--------- --------- ---------
1 0 3
1 1 3
ciao, Bernhard