Du hast eine schöne View gebaut, die Daten aus drei Tabellen zusammenführt – und dann kommt jemand und will darauf ein INSERT machen. Oracle sagt Nein. Du sagst: Kenn ich ein Mittel.
Willkommen in der Welt der Instead-Of Trigger.
Das Problem: Views sind standardmäßig schreibgeschützt
Wer schon länger mit Oracle arbeitet, kennt das Szenario: Du erstellst eine View, die mehrere Tabellen per JOIN verknüpft, aggregiert oder transformiert – und plötzlich möchte eine Anwendung nicht nur lesen, sondern auch schreiben. Oracle blockiert das mit einem unmissverständlichen Fehler:
-- Versuch, in eine Join-View zu schreiben: INSERT INTO v_mitarbeiter_abteilung (name, abteilung_name) VALUES ('Max Müller', 'IT'); -- Oracle antwortet: ORA-01779: cannot modify a column which maps to a non key-preserved table
Klar, das macht technisch Sinn: Oracle weiß nicht, in welche der zugrunde liegenden Tabellen die Daten sollen. Aber genau hier kommen Instead-Of Trigger ins Spiel – sie sagen Oracle: „Lass mich das übernehmen.“
Was ist ein Instead-Of Trigger?
Ein Instead-Of Trigger ist ein spezieller PL/SQL-Trigger, der anstelle der eigentlichen DML-Operation (INSERT, UPDATE, DELETE) auf einer View ausgeführt wird. Er fängt den Schreibversuch ab – und du entscheidest im Trigger-Body, was wirklich passiert.
Kurz gesagt: Statt Oracle die DML-Operation auf der View ausführen zu lassen (was es sowieso verweigert), springt dein Trigger ein und verteilt die Daten selbst auf die richtigen Basistabellen.
Ein konkretes Beispiel
Nehmen wir ein klassisches Setup: Mitarbeiter und Abteilungen in separaten Tabellen, eine View, die beides zusammenführt.
-- Basistabellen CREATE TABLE abteilungen ( abt_id NUMBER PRIMARY KEY, abt_name VARCHAR2(100) ); CREATE TABLE mitarbeiter ( ma_id NUMBER PRIMARY KEY, ma_name VARCHAR2(100), abt_id NUMBER REFERENCES abteilungen(abt_id) ); -- Die View - lesbar, aber nicht ohne Weiteres schreibbar CREATE OR REPLACE VIEW v_mitarbeiter AS SELECT m.ma_id, m.ma_name, a.abt_id, a.abt_name FROM mitarbeiter m JOIN abteilungen a ON m.abt_id = a.abt_id;
Jetzt der Trigger. Wir wollen ermöglichen, dass man über die View neue Mitarbeiter anlegen kann:
CREATE OR REPLACE TRIGGER trg_v_mitarbeiter_ins
INSTEAD OF INSERT ON v_mitarbeiter
FOR EACH ROW
DECLARE
l_abt_id abteilungen.abt_id%TYPE;
BEGIN
-- Prüfen ob Abteilung schon existiert
BEGIN
SELECT abt_id INTO l_abt_id
FROM abteilungen
WHERE abt_name = :NEW.abt_name;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Neue Abteilung anlegen
l_abt_id := abt_seq.NEXTVAL;
INSERT INTO abteilungen (abt_id, abt_name)
VALUES (l_abt_id, :NEW.abt_name);
END;
-- Mitarbeiter einfügen
INSERT INTO mitarbeiter (ma_id, ma_name, abt_id)
VALUES (:NEW.ma_id, :NEW.ma_name, l_abt_id);
END;
/
Ab jetzt funktioniert das INSERT über die View reibungslos:
-- Das klappt jetzt problemlos! INSERT INTO v_mitarbeiter (ma_id, ma_name, abt_name) VALUES (42, 'Anna Schmidt', 'IT'); -- Oracle meldet: 1 row created.
UPDATE und DELETE ebenfalls möglich
Natürlich kannst du für alle drei DML-Operationen eigene Instead-Of Trigger anlegen – oder alles in einem Trigger kombinieren:
CREATE OR REPLACE TRIGGER trg_v_mitarbeiter
INSTEAD OF INSERT OR UPDATE OR DELETE ON v_mitarbeiter
FOR EACH ROW
BEGIN
IF INSERTING THEN
-- Insert-Logik (wie oben)
NULL;
ELSIF UPDATING THEN
-- Nur den Mitarbeiternamen anpassen
UPDATE mitarbeiter
SET ma_name = :NEW.ma_name
WHERE ma_id = :OLD.ma_id;
ELSIF DELETING THEN
-- Mitarbeiter löschen (Abteilung bleibt)
DELETE FROM mitarbeiter
WHERE ma_id = :OLD.ma_id;
END IF;
END;
/
Typische Anwendungsfälle
- Join-Views schreibbar machen – Daten aus mehreren Tabellen über eine View einfügen oder ändern. Der Klassiker.
- Security-Layer – Nur bestimmte Spalten über die View beschreibbar lassen, andere implizit sperren.
- Daten-Transformation – Eingehende Werte vor dem Schreiben umrechnen, validieren oder mit Standardwerten anreichern.
- Auditierung – Jeden Schreibzugriff zentral protokollieren, egal woher er kommt.
- Object Views – Der einzige Weg, Views auf Basis von Oracle-Objekttypen beschreibbar zu machen.
Was du beachten solltest
⚠ Kein Statement-Level: Instead-Of Trigger feuern immer FOR EACH ROW. Eine statement-level Variante gibt es nicht – auch wenn du das FOR EACH ROW weglässt, verhält sich Oracle so, als wäre es angegeben.
💡 Tipp: Du kannst mehrere Instead-Of Trigger auf derselben View kombinieren (z. B. getrennte Trigger für INSERT und DELETE). Oracle führt sie in der Reihenfolge ihrer Erstellung aus.
Außerdem solltest du im Hinterkopf behalten: Je komplexer die Logik im Trigger, desto mehr wird Fehlersuche zur Detektivarbeit. Halte die Trigger-Bodies so schlank wie möglich und lagere komplexe Logik in Packages aus.
Fazit
Instead-Of Trigger sind eines dieser Oracle-Features, das du entweder kennst und liebst – oder das dir irgendwann aus einer verzwickten Situation hilft und ab dann nicht mehr aus deinem Werkzeugkasten wegzudenken ist. Sie machen Views schreibbar, ohne die Kapselung zu opfern, und geben dir vollständige Kontrolle darüber, was bei einem Schreibzugriff auf eine View wirklich passiert.
Gerade in Anwendungen, die ihre Datenbankschicht hinter Views verstecken (aus gutem Grund!), sind
Instead-Of Trigger das Bindeglied, das die Abstraktion vollständig macht.
