Falls du schon mal in der Situation warst, dass du eine Aktion für jede einzelne Zeile bei einem Massen-Update ausführen musstest – oder eben nur einmal für die ganze Tabelle – dann bist du bestimmt über die Frage gestolpert: Wie mache ich das am besten? PostgreSQL bietet dir zwei Möglichkeiten: Trigger auf Zeilenebene und Trigger auf Statement-Ebene. Zu wissen, wann du welchen Ansatz nutzt, ist wichtig für ein gutes Datenbank-Design, Performance-Optimierung und um Fehler zu vermeiden. Lass uns das mal anschauen!
Trigger, die auf Zeilenebene (FOR EACH ROW) laufen, werden jedes Mal für jede betroffene Zeile bei INSERT, UPDATE oder DELETE ausgeführt. Das heißt: Wenn dein SQL-Query 100 Zeilen betrifft, läuft der Trigger auch 100 Mal.
Wann benutzt man das?
Row-Trigger sind praktisch, wenn du jede geänderte Zeile einzeln bearbeiten willst. Zum Beispiel:
- Änderungen für jede Zeile loggen.
- Verknüpfte Daten für jede Zeile automatisch aktualisieren.
Beispiel: Änderungen für jede Zeile loggen
Angenommen, wir haben eine Tabelle für Studierende:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
age INT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Wir wollen jede Zeile, die in dieser Tabelle geändert wird, in einer separaten Tabelle students_log protokollieren:
CREATE TABLE students_log (
log_id SERIAL PRIMARY KEY,
student_id INT,
old_name VARCHAR(100),
new_name VARCHAR(100),
changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Funktion zum Loggen der Änderungen:
CREATE OR REPLACE FUNCTION log_student_update()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO students_log(student_id, old_name, new_name, changed_at)
VALUES (OLD.id, OLD.name, NEW.name, CURRENT_TIMESTAMP);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Trigger mit FOR EACH ROW erstellen:
CREATE TRIGGER student_update_logger
AFTER UPDATE ON students
FOR EACH ROW
EXECUTE FUNCTION log_student_update();
Testen:
UPDATE students
SET name = 'Ivan Ivanov'
WHERE id = 1;
Nach diesem Query taucht in der Tabelle students_log ein Eintrag mit den Details der Änderung auf.
Statement-Trigger (FOR EACH STATEMENT)
Statement-Trigger (FOR EACH STATEMENT) werden nur einmal für das ganze SQL-Statement ausgeführt, egal wie viele Zeilen betroffen sind. Wenn dein Query 100 Zeilen updated, läuft der Trigger trotzdem nur einmal.
Statement-Trigger sind praktisch, wenn du:
- Eine Aktion nur einmal für die ganze Operation ausführen willst.
- Mit aggregierten Daten arbeiten oder Berechnungen für die ganze Tabelle machen willst.
Beispiel: Änderungszähler aktualisieren
Angenommen, wir haben eine Tabelle für einen Änderungszähler für die Tabelle students:
CREATE TABLE students_changes_log (
total_changes INT DEFAULT 0
);
INSERT INTO students_changes_log(total_changes) VALUES (0);
Wir wollen diesen Zähler jedes Mal erhöhen, wenn ein UPDATE auf der Tabelle students ausgeführt wird.
Funktion zum Aktualisieren des Zählers:
CREATE OR REPLACE FUNCTION increment_changes_counter()
RETURNS TRIGGER AS $$
BEGIN
UPDATE students_changes_log
SET total_changes = total_changes + 1;
RETURN NULL; -- Statement-Trigger geben keine Zeilen zurück
END;
$$ LANGUAGE plpgsql;
Trigger mit FOR EACH STATEMENT erstellen:
CREATE TRIGGER update_changes_counter
AFTER UPDATE ON students
FOR EACH STATEMENT
EXECUTE FUNCTION increment_changes_counter();
Testen:
UPDATE students
SET age = age + 1
WHERE age < 20;
Nach dem Ausführen des Queries wird der Trigger nur einmal ausgelöst und der Änderungszähler um eins erhöht.
Vergleich FOR EACH ROW und FOR EACH STATEMENT
| Kriterium | FOR EACH ROW | FOR EACH STATEMENT |
|---|---|---|
| Ausführungsebene | Für jede betroffene Zeile | Einmal pro Operation |
| Aufrufhäufigkeit | Ein Aufruf pro Zeile | Ein Aufruf pro SQL-Statement |
| Aufgaben | Einzelne Änderungen loggen, Zeilenverarbeitung | Aggregation, Metainformationen aktualisieren |
| Beispiel | Änderungen für jede Zeile loggen | Änderungszähler aktualisieren |
| Performance | Aufwändiger bei Massenoperationen | Weniger aufwändig bei Massenoperationen |
Wann FOR EACH ROW und wann FOR EACH STATEMENT?
Nutze FOR EACH ROW, wenn:
- Du willst, dass der Trigger für jede Zeile ausgeführt wird.
- Die Logik an Änderungen einzelner Zeilen hängen soll.
- Du Zugriff auf
OLDundNEWDaten für jede Zeile brauchst.
Beispiel: Änderungen in einer Tabelle loggen oder automatisch verknüpfte Einträge anlegen.
Nutze FOR EACH STATEMENT, wenn:
- Du eine Aktion nur einmal für die ganze Operation ausführen willst.
- Die Trigger-Logik nicht von einzelnen Zeilen abhängt.
- Performance kritisch ist und viele Trigger-Aufrufe unerwünscht sind.
Beispiel: Zähler aktualisieren, Metadaten für die Tabelle berechnen.
Fehler und wichtige Punkte
Den richtigen Trigger-Typ zu wählen ist manchmal tricky, hier ein paar Dinge, die du beachten solltest:
- Einer der häufigsten Fehler ist, in einem
FOR EACH STATEMENT-Trigger aufOLDundNEWDaten zugreifen zu wollen. Das führt zu einem Fehler, weil diese Variablen nur in Row-Triggern verfügbar sind. - Row-Trigger (
FOR EACH ROW) können bei vielen betroffenen Zeilen die Performance stark beeinträchtigen. Immer an die Performance denken! - Sei vorsichtig mit möglicher Trigger-Rekursion. Wenn ein Trigger z.B. Daten in derselben Tabelle ändert, kann das zu einer Endlosschleife führen.
GO TO FULL VERSION