- 1 Section
- 10 Lessons
- unbegrenzt
- SQL Fortgeschritten10
- 1.1Alle JOIN-Typen: RIGHT, FULL OUTER, CROSS, SELF
- 1.2Unterabfragen (Subqueries)
- 1.3Common Table Expressions (CTEs)
- 1.4Window Functions: ROW_NUMBER, RANK, PARTITION BY
- 1.5Views erstellen und nutzen
- 1.6Stored Procedures und Funktionen
- 1.7Trigger
- 1.8Datenbankindizes und Query-Optimierung
- 1.9Datenbankreporte und komplexe Auswertungen
- 1.10SQL-Fortgeschrittenenaufgaben
Trigger
Ein Trigger (deutsch „Auslöser") ist ein Stück Datenbank-Logik, das automatisch ausgeführt wird, wenn auf einer Tabelle bestimmte Ereignisse passieren – ein INSERT, UPDATE oder DELETE. Anders als eine Stored Procedure ruft niemand den Trigger explizit auf; er feuert von selbst, wenn das Ereignis eintritt.
Trigger sind das letzte fortgeschrittene Datenbank-Konzept dieses Kurses. Sie sind mächtig, aber auch riskant – falsch verwendet machen sie das Verhalten der DB schwer nachvollziehbar. Diese Lektion zeigt dir die Grundsyntax, die wichtigsten Anwendungsfälle (Audit-Log, Auto-Updates, Validierung) und wo die Stolperfallen liegen.
1) Was ist ein Trigger?
Ein Trigger besteht aus drei Teilen:
- Zeitpunkt:
BEFOREoderAFTER– wird der Code vor oder nach der eigentlichen Operation ausgeführt? - Ereignis:
INSERT,UPDATEoderDELETE– auf welche Operation reagiert der Trigger? - Aktion: der eigentliche Code, der laufen soll (typischerweise
BEGIN…END-Block wie bei einer Stored Procedure)
Daraus ergeben sich sechs typische Trigger-Varianten: BEFORE INSERT, AFTER INSERT, BEFORE UPDATE, AFTER UPDATE, BEFORE DELETE, AFTER DELETE. Jeder hat seinen eigenen Anwendungszweck.
2) Die Trigger-Matrix
Wann setze ich welchen Trigger ein? Übersicht mit Beispielen:
NEW verfügbarupdated_at setzen.OLD + NEWOLD verfügbarNEW verfügbarOLD + NEWOLD verfügbarNEW enthält die neuen Werte (bei INSERT/UPDATE), OLD die alten (bei UPDATE/DELETE). In MySQL als NEW.spaltenname/OLD.spaltenname verfügbar, in PostgreSQL als NEW/OLD Records.3) Beispiel-Daten
Wir nutzen drei Tabellen: artikel (mit Bestand), bestellungen (mit Menge), und eine audit_log-Tabelle, in die alle Änderungen geschrieben werden:
| id | bezeichnung | bestand | updated_at |
|---|---|---|---|
| 1 | Laptop | 10 | 2026-01-01 |
| 2 | Maus | 50 | 2026-01-01 |
| id | tabelle | aktion | zeit | details |
|---|---|---|---|---|
| (zunächst leer) | ||||
4) Erster Trigger: Audit-Log bei INSERT
Ein klassischer Anwendungsfall: jedes Mal, wenn eine neue Bestellung eingefügt wird, soll ein Eintrag ins Audit-Log geschrieben werden:
DELIMITER // CREATE TRIGGER trg_bestellung_insert AFTER INSERT ON bestellungen FOR EACH ROW BEGIN INSERT INTO audit_log (tabelle, aktion, zeit, details) VALUES ( 'bestellungen', 'INSERT', NOW(), CONCAT('id=', NEW.id, ', kunde=', NEW.kunde_id) ); END // DELIMITER ;
Wenn nun jemand INSERT INTO bestellungen … ausführt, schreibt die DB automatisch einen Eintrag ins Audit-Log. Die Anwendung merkt davon nichts.
AFTER INSERT: Trigger feuert nach dem Einfügen – die Zeile existiert dann schon, hat eine IDFOR EACH ROW: Trigger läuft für jede betroffene Zeile (bei einem Bulk-Insert von 100 Zeilen feuert er 100-mal)NEW.id: Zugriff auf die Werte der gerade eingefügten Zeile
5) BEFORE-Trigger: Defaults und Validierung
BEFORE-Trigger sind ideal, um Daten zu vervollständigen oder zu prüfen, bevor sie in die Tabelle geschrieben werden. Hier ein BEFORE INSERT, der automatisch ein created_at setzt und das Pflichtfeld name trimmt:
CREATE TRIGGER trg_kunde_before_insert BEFORE INSERT ON kunden FOR EACH ROW BEGIN -- Default-Wert setzen, falls keiner gegeben IF NEW.created_at IS NULL THEN SET NEW.created_at = NOW(); END IF; -- Whitespace vom Namen abschneiden SET NEW.name = TRIM(NEW.name); -- Validierung: kein leerer Name IF NEW.name = '' THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Name darf nicht leer sein'; END IF; END
Im BEFORE-Trigger kannst du NEW-Werte ändern (SET NEW.spalte = …), bevor sie tatsächlich gespeichert werden. In einem AFTER-Trigger geht das nicht mehr – die Zeile ist dann schon geschrieben.
Mit SIGNAL SQLSTATE '45000' wirfst du einen Fehler. Das INSERT wird abgebrochen, die Anwendung bekommt eine Fehlermeldung.
6) UPDATE-Trigger: alten und neuen Wert vergleichen
Bei UPDATE-Triggern hast du beides verfügbar: OLD für den Wert vor, NEW für den Wert nach der Änderung. Klassisches Beispiel: automatisches Setzen eines updated_at-Zeitstempels:
CREATE TRIGGER trg_artikel_before_update BEFORE UPDATE ON artikel FOR EACH ROW BEGIN -- Automatisch Zeitstempel aktualisieren SET NEW.updated_at = NOW(); -- Nur ins Audit-Log schreiben, wenn sich der Bestand wirklich ändert IF OLD.bestand <> NEW.bestand THEN INSERT INTO audit_log (tabelle, aktion, zeit, details) VALUES ( 'artikel', 'BESTAND_GEÄNDERT', NOW(), CONCAT('artikel ', OLD.id, ' von ', OLD.bestand, ' auf ', NEW.bestand) ); END IF; END
Wichtig: ein UPDATE feuert den Trigger für jede betroffene Zeile. Ein UPDATE artikel SET bestand = bestand - 1 über 100 Zeilen löst den Trigger 100-mal aus.
7) DELETE-Trigger: Lösch-Protokoll
Bei DELETE ist nur OLD verfügbar – schließlich wird die Zeile gerade entfernt. Typischer Einsatz: archivieren, was gelöscht wird:
CREATE TRIGGER trg_kunde_before_delete BEFORE DELETE ON kunden FOR EACH ROW BEGIN -- Gelöschte Kunden in eine Archiv-Tabelle kopieren INSERT INTO kunden_archiv (id, name, ort, geloescht_am) VALUES (OLD.id, OLD.name, OLD.ort, NOW()); END
Auch eine sinnvolle Anwendung: das Löschen unter bestimmten Bedingungen verhindern (z. B. Kunden mit offenen Bestellungen):
CREATE TRIGGER trg_kunde_delete_schutz BEFORE DELETE ON kunden FOR EACH ROW BEGIN DECLARE v_offene INT; SELECT COUNT(*) INTO v_offene FROM bestellungen WHERE kunde_id = OLD.id AND status <> 'abgeschlossen'; IF v_offene > 0 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Kunde hat offene Bestellungen'; END IF; END
8) BEFORE vs. AFTER – wann was?
Die Wahl ist nicht beliebig:
| BEFORE eignet sich für … | AFTER eignet sich für … |
|---|---|
Daten verändern, bevor sie geschrieben werden (z. B. updated_at setzen) | Andere Tabellen aktualisieren auf Basis des Ergebnisses |
Validierung mit möglichem Abbruch (SIGNAL) | Audit-Logs schreiben (Zeile existiert sicher) |
| Default-Werte berechnen | Verkettete Aktionen, die die ID des neuen Datensatzes brauchen |
| Constraints prüfen, die mit DB-Constraints nicht abbildbar sind | Cache-Tabellen oder Materialized Views aktualisieren |
9) Typische Anwendungen in der Praxis
created_at bei Insert, updated_at bei jedem Update – ohne dass die Anwendung daran denken muss.CHECK-Constraints nicht ausdrückbar sind (z. B. Tabellen-übergreifend).10) Trigger und Performance
Trigger laufen innerhalb jeder Transaktion, die die Tabelle ändert. Das hat zwei wichtige Konsequenzen:
- Ein langsamer Trigger macht jede Schreiboperation auf der Tabelle langsam – nicht nur diejenigen, die ihn direkt brauchen
- Wenn der Trigger fehlschlägt oder einen Fehler wirft, scheitert die gesamte Schreiboperation
Bei einem Bulk-UPDATE über 1 Million Zeilen läuft der Trigger 1 Million Mal. Schreibt er jedesmal in ein Audit-Log, hast du 2 Millionen Schreibvorgänge. Das kann massiv ausbremsen.
INSERT noch nebenbei in zwei andere Tabellen geschrieben wird. Das macht Debugging schwer. Dokumentiere Trigger immer ausführlich.
11) Trigger verwalten
Wie bei Procedures: Trigger werden dauerhaft gespeichert.
-- Vorhandene Trigger anzeigen SHOW TRIGGERS; -- Auf bestimmte Tabelle einschränken SHOW TRIGGERS LIKE 'bestellungen'; -- Trigger löschen DROP TRIGGER IF EXISTS trg_bestellung_insert;
Ändern bedeutet meist: DROP und neu CREATE. Manche DBs unterstützen CREATE OR REPLACE TRIGGER (PostgreSQL, Oracle), MySQL nicht direkt.
12) Dialekt-Unterschiede
Wie bei Stored Procedures unterscheidet sich die Trigger-Syntax zwischen Datenbanken. Wichtige Punkte:
- MySQL/MariaDB:
FOR EACH ROWist Pflicht – es gibt nur row-level Trigger, keine statement-level - PostgreSQL: Trigger ruft eine separate Funktion auf (zweistufig:
CREATE FUNCTION+CREATE TRIGGER), unterstütztFOR EACH STATEMENT - SQL Server: andere Syntax mit Pseudo-Tabellen
INSERTEDundDELETEDstattNEW/OLD - Oracle: PL/SQL-Syntax, sehr ausführlich, unterstützt mehrere Ereignisse pro Trigger
Konzept ist überall gleich, Schlüsselwörter unterscheiden sich. In Klausuren wird meist MySQL-Syntax erwartet.
13) Häufige Fehler
- Rekursive Trigger: ein Trigger auf Tabelle A ändert Tabelle B, deren Trigger wiederum Tabelle A ändert – Endlos-Schleife. Manche DBs erkennen das und brechen ab, andere stürzen ab.
- OLD/NEW falsch verwendet: bei
INSERTgibt es keinOLD, beiDELETEkeinNEW. Fehler zur Laufzeit. - SET im AFTER-Trigger: in
AFTER-Triggern kannst duNEW-Werte nicht mehr ändern – nur lesen. Die Zeile ist schon geschrieben. - Trigger ignoriert bei direktem Daten-Import: viele Bulk-Loader (z. B.
LOAD DATA INFILE) umgehen Trigger aus Performance-Gründen. - Versteckte Fehler: wenn der Trigger leise wegfällt (z. B. nach Datenbank-Migration), bemerkt das niemand sofort. Critical-Pfad-Logik gehört nicht in Trigger.
- Audit-Tabelle wird Engpass: jeder Schreibvorgang schreibt zusätzlich ins Log. Tabelle muss gut indiziert sein und sollte regelmäßig archiviert werden.
14) Wann besser KEIN Trigger?
Trigger sind verführerisch, aber oft gibt es bessere Alternativen:
- Default-Werte:
DEFAULT NOW()in der Spaltendefinition reicht oft, kein Trigger nötig - Einfache Constraints:
CHECK,NOT NULL,UNIQUE,FOREIGN KEYsind klarer und schneller als ein Trigger - Geschäftslogik: gehört eher in die Anwendung (testbar, versionierbar) als in die DB
- Benachrichtigungen: ein Message-Queue-System (RabbitMQ, Kafka) ist meist robuster als DB-Trigger
- Replikation und CDC: für „Was hat sich geändert?" gibt es spezialisierte Tools (Debezium, native CDC), die zuverlässiger sind
15) Sicherheit und SQL Injection
Trigger werden im Kontext desjenigen Benutzers ausgeführt, der die ursprüngliche Operation veranlasst hat (oder im Definer-Kontext, je nach DB). Sicherheits-Hinweis: Werte aus NEW. sind nicht automatisch SQL-Injection-sicher, wenn sie z. B. in dynamisches SQL eingebaut werden. In Triggern dynamisches SQL nach Möglichkeit vermeiden, parametrisierte Inserts/Updates nutzen. Mehr Hintergrund in Secure Coding (K11).
Zusammenfassung
Ein Trigger ist ein automatisch ausgeführter Code-Block, der bei INSERT, UPDATE oder DELETE auf einer Tabelle feuert – jeweils BEFORE oder AFTER der Operation. In BEFORE-Triggern lassen sich Werte über NEW noch verändern und Vorgänge per SIGNAL abbrechen; AFTER-Trigger eignen sich für Audit-Logs und abhängige Aktionen. Verfügbar sind NEW (neue Werte) und OLD (alte Werte) – je nach Ereignis. Typische Anwendungen: Audit-Logs, automatische Zeitstempel, Validierung, Lösch-Schutz. Vorsicht: Trigger sind unsichtbar im normalen SQL-Code und können erhebliche Performance-Effekte bei Bulk-Operationen haben. Für reine Geschäftslogik ist Code in der Anwendung meist besser geeignet.
