Oracle Trigger - Table mutating

Paul159

Neuer Benutzer
Beiträge
4
Hallo zusammen,

wahrscheinlich eine einfache Sache, aber ich bin leider kein Trigger-Spezialist.

Die Aufgabe:
Es gibt einen Trigger der bei einen INSERT/UPDATE auf Tabelle A reagieren soll, ein weiteres SELECT auf Tabelle A ausführt und dann die gesammelten Daten Tabelle B hinzufügt.

Da ich ein SELECT auf die gleiche Tabelle ausführe, die auch den Trigger auslöst, bekomme ich die Fehlermeldung:
ORA-04091: table is mutating, trigger/function may not see it

Tja, wie kann ich am besten diesen Fehler vermeiden?
(Leider muss es ein Trigger sein)

Mein ungefährer Code:
Code:
CREATE OR REPLACE TRIGGER mytrigger
AFTER UPDATE OF id, name ON a
FOR EACH ROW
WHEN (bedingung)
BEGIN
-- INSERT auf Tabelle B, mit Daten von SELECT auf Tabelle A
END


Danke schonmal!
 
Werbung:
Warum überhaupt ein SELECT? Wenn Du nur den gerade geänderten Datensatz zusätzlich in der anderen Tabelle speichern willst, ist das gar nicht notwendig:

Code:
CREATE OR REPLACE TRIGGER mytrigger
AFTER UPDATE OF id, name ON a
FOR EACH ROW
WHEN (bedingung)
BEGIN
  INSERT into table_b(id, name) values (new.id, new.name);
END;
 
Danke für eure Antworten!

Ich war vorhin sehr in Eile, deswegen versuche ich das jetzt noch bisschen klarer zu machen:

Tabelle A
Code:
type   |   value   |   check
   1    |    5     |      1        <- 2. Den Wert muss ich in Tabelle B bringen
   3    |    7     |     0
   1    |    2     |     0        <- 1. value = 2 ist mein Startschuss und über (type = 1 AND check = 1) bekomme ich die value 5, die ich in der Tabelle B "loggen" muss.
   1    |    5     |     0

value = 2 ist quasi meine Bedingung für WHEN.
Und FOR EACH ROW brauch ich um über :NEW auf den type zu kommen, richtig?


Ich muss bisschen wegen Datenschutz aufpassen, deswegen kann ich nicht den wirklichen Code/Struktur zeigen. Sorry deswegen.
 
so ganz kann ich noch nicht folgen. Was wenn type=1 und check=1 mehrfach auftreten und dabei unterschiedliche value-Werte haben, was dann?

trifft das halbwegs, was du suchst?

Code:
andreas@[local]:5432/test# create table paul (type int, value int, check_ bool);CREATE TABLE
andreas@[local]:5432/test# create table paul_b(ts timestamp, value int);CREATE TABLE
andreas@[local]:5432/test# create or replace function paul_trigger() returns trigger as $$begin insert into paul_b select now(), value from paul where check_ and type=new.type; return new; end; $$language plpgsql;
CREATE FUNCTION
andreas@[local]:5432/test# create trigger paul_trigger before insert on paul for each row execute procedure paul_trigger();
CREATE TRIGGER
andreas@[local]:5432/test# insert into paul values (3,7,false);
INSERT 0 1
andreas@[local]:5432/test# insert into paul values (1,2,false);
INSERT 0 1
andreas@[local]:5432/test# insert into paul values (1,5,true);
INSERT 0 1
andreas@[local]:5432/test# select * from paul_b ;
 ts | value
----+-------
(0 rows)

andreas@[local]:5432/test# insert into paul values (1,2,false);
INSERT 0 1
andreas@[local]:5432/test# select * from paul_b ;
             ts             | value
----------------------------+-------
 2018-07-24 20:50:22.856318 |     5
(1 row)

andreas@[local]:5432/test#

PostgreSQL, kein Oraggle...
 
Im Prinzip schon und ich dachte selbst auch, dass das so gehen sollte, aber bei der Oracle DB kommt halt der erwähnte Fehler.
Hätte gern Postgres, oder ne Datenbankfortbildung... ;-)

so ganz kann ich noch nicht folgen. Was wenn type=1 und check=1 mehrfach auftreten und dabei unterschiedliche value-Werte haben, was dann?
Es war nur ein ungefähres Beispiel um mein Problem zu schildern. In echt ist das alles bisschen komplexer.

Ich weiß nicht nach was ich vorhin gesucht habe, aber anscheinend nach was falschen.
Anscheinend ist das hier der wahrscheinlich richtige Weg. Kann kaum glauben, dass das so umständlich sein soll. :-(
Muss noch ausprobieren ob das in meinen Fall überhaupt funktioniert.

Vielleicht weiß ja jemand noch einen besseren Weg.

Aber Vielen Dank fürs ausprobieren!
 
Zuletzt bearbeitet:
Hätte gern Postgres, oder ne Datenbankfortbildung... ;-)

Da kann ich helfen, PostgreSQL bekommst Du hier: PostgreSQL: Downloads und Schulungen dazu bei uns ;-)

Anscheinend ist das hier der wahrscheinlich richtige Weg.

PostgreSQL kennt seit Version 10 transition tables, guggst Du hier: select * from depesz; » Blog Archive » Waiting for PostgreSQL 10 – Implement syntax for transition tables in AFTER triggers.


Andreas
 
Werbung:
Moin.
Die Transitiontables sind eine feine Sache, aber in Oracle lässt sich das mit wenigen Zeilen auch selbst implementieren:
Code:
create global temporary table test_gtt as select * from test where 1=0;
Damit hat man schon mal den Container für die geänderten Daten. In "echt" würde man nur die wirklich benötigten Spalten anlegen. Jetzt noch der Trigger:

Code:
create or replace trigger test_trg after insert or update or delete on test for each row
begin
 insert into test_gtt (spaltexy) values(:new.spaltexy);
end;
/

Damit wird der neue Wert in die temp Tabelle geschrieben und kann anschließend von dort gelesen werden. Bei vielen Spalten ist das natürlich etwas Schreibaufwand und falls neue Spalten hinzukommen sind diese auch entsprechend einzubauen.
Nach jedem Commit ist die temp Tabelle wieder leer, unterschiedliche Sessions die gleichzeitig auf die Tabelle zugreifen sehen nur ihre jeweils eigenen Änderungen.
 
Zurück
Oben