Menu

SQLite Trigger: CREATE TRIGGER mit BEFORE/AFTER & OLD/NEW

Wie Trigger in SQLite funktionieren – BEFORE und AFTER, INSTEAD OF auf Views, die Referenzen OLD und NEW und wann sich ein Trigger wirklich lohnt.

Diese Seite enthält ausführbare Editoren — bearbeiten, ausführen und Ausgabe sofort sehen.

Ein Trigger führt SQL automatisch aus

Ein SQLite Trigger ist ein gespeicherter SQL-Block, der immer dann ausgelöst wird, wenn ein bestimmtes Ereignis an einer bestimmten Tabelle passiert. Du schreibst ihn einmal – das „Wann" übernimmt SQLite für dich.

Der grundsätzliche Aufbau:

Wir haben nirgends ein explizites INSERT auf price_history ausgeführt – das hat der Trigger für uns übernommen. Jede künftige Preisänderung landet auf dieselbe Weise im Log, egal ob sie aus der CLI, einem Skript oder einer App kommt.

Aufbau von CREATE TRIGGER

Schauen wir uns die Syntax Stück für Stück an:

CREATE TRIGGER trigger_name
{ BEFORE | AFTER | INSTEAD OF } { INSERT | UPDATE [ OF column_list ] | DELETE }
ON table_name
[ FOR EACH ROW ]
[ WHEN condition ]
BEGIN
    -- eine oder mehrere Anweisungen
END;
  • TimingBEFORE läuft vor der Änderung, AFTER danach, und INSTEAD OF ersetzt sie komplett (nur bei Views).
  • Event — die Operation, die den Trigger auslöst. Mit UPDATE OF col1, col2 schränkst du das Ganze auf bestimmte Spalten ein.
  • Tabelle — die Tabelle, die überwacht wird.
  • FOR EACH ROW — SQLite kennt ausschließlich Row-Level-Trigger, das ist also sowieso implizit. Du darfst es zur Klarheit hinschreiben, ändern tut es nichts.
  • WHEN — eine optionale Bedingung. Der Trigger-Body wird nur ausgeführt, wenn sie zutrifft.
  • Body — eine oder mehrere Anweisungen zwischen BEGIN und END. Jede muss mit einem Semikolon abgeschlossen werden.

Mehr Grammatik gibt's nicht. Die meisten echten Trigger kommen mit fünf bis zehn Zeilen aus.

OLD und NEW: die betroffene Zeile

Innerhalb des Bodys stehen dir zwei Pseudo-Zeilen zur Verfügung, mit denen du auf die Daten zugreifst:

  • NEW — die neue Zeile. Verfügbar in INSERT- und UPDATE-Triggern.
  • OLD — die bisherige Zeile. Verfügbar in UPDATE- und DELETE-Triggern.

Ein DELETE-Trigger kennt nur OLD, ein INSERT-Trigger nur NEW. Ein UPDATE-Trigger hat beide.

Die gelöschte Zeile ist aus accounts verschwunden, aber ihre Daten wurden vorher in deletions festgehalten.

BEFORE UPDATE Trigger: Werte prüfen oder anpassen

Ein BEFORE-Trigger feuert, bevor die Änderung tatsächlich in die Datenbank geschrieben wird. Praktisch, um einen Fehler auszulösen oder Daten vorher noch zu normalisieren:

Das zweite INSERT bricht ab, bevor auch nur eine Zeile geschrieben wird. RAISE(ABORT, '...') bricht das laufende Statement ab und macht alle Änderungen daraus rückgängig — mit RAISE(FAIL, ...), RAISE(ROLLBACK, ...) und RAISE(IGNORE) steuerst du noch genauer, was passieren soll.

Für reine Datenvalidierung sind CHECK-Constraints die bessere Wahl: Sie sind deklarativ, und der Optimizer kennt sie. Ein BEFORE-Trigger lohnt sich erst dann, wenn die Regel auf andere Tabellen zugreifen muss oder etwas leisten soll, was ein CHECK nicht abbilden kann.

WHEN: Trigger mit Bedingung

Mit einer WHEN-Klausel legst du fest, welche Zeilenänderungen den Trigger-Rumpf überhaupt auslösen. Sie wird pro Zeile ausgewertet, nachdem OLD und NEW gebunden wurden:

Die erste Bestellung fällt durchs Raster. Die beiden anderen landen drin. Ohne die WHEN-Klausel würde jedes Insert blind in big_orders geschrieben, und du müsstest erst beim Lesen filtern.

INSTEAD OF: Views beschreibbar machen

Views sind in SQLite standardmäßig nur lesbar. Ein INSTEAD OF-Trigger fängt einen Schreibzugriff auf einen View ab und führt stattdessen dein SQL aus — meist übersetzt er den Zugriff in Schreibvorgänge auf die zugrunde liegende(n) Tabelle(n):

Die Anwendung spricht mit der View, als wäre sie eine Tabelle. Im Hintergrund kümmert sich der Trigger darum, den Wert sauber in first_name und last_name aufzuteilen.

SQLite Trigger anzeigen und löschen

Trigger werden – genau wie Tabellen und Indizes – in sqlite_master abgelegt:

DROP TRIGGER IF EXISTS name; ist die sichere Variante. Wenn du die zugehörige Tabelle löschst, verschwindet der Trigger automatisch mit – aufräumen musst du also nicht von Hand.

Stolpersteine, die du kennen solltest

Ein paar Dinge erwischen einen beim ersten Mal:

  • Trigger feuern pro Zeile, nicht pro Statement. Ein UPDATE, das 1.000 Zeilen betrifft, löst den Trigger 1.000 Mal aus. Wenn der Trigger-Body selbst aufwendig ist, summiert sich das schnell.
  • Trigger laufen innerhalb der umgebenden Transaktion. Rollt das äußere Statement zurück, werden auch die Schreibvorgänge des Triggers zurückgerollt. Meistens ist genau das gewollt – aber ein Trigger ist eben kein Notausgang für „bitte auf jeden Fall mitloggen".
  • Rekursive Trigger sind standardmäßig aus. Ein Trigger, der dieselbe Tabelle verändert, ruft sich nicht selbst erneut auf, solange du nicht PRAGMA recursive_triggers = ON; setzt. Lass das aus, wenn du keinen konkreten Grund hast.
  • Schreibzugriffe aus der Anwendung können Trigger nur umgehen, indem sie die Datenbank komplett umgehen. Solange jeder Write durch SQLite geht, läuft der Trigger. Auch ORMs, die per Raw-SQL batchen, lösen ihn aus.
  • Verteile keine Geschäftslogik über viele Trigger. Vom Aufrufort aus sind sie unsichtbar – wer debuggen muss, „warum ist diese Zeile plötzlich da?", darf erst mal sqlite_master durchgreppen. Nimm sie für Querschnittsthemen (Audit-Logs, abgeleitete Spalten, beschreibbare Views) und halte den Rest in der Anwendungslogik.

Ein realistisches Beispiel: Audit-Log

Bringen wir die Patterns zusammen – jede Änderung an einer posts-Tabelle wird protokolliert:

Ein einziger Trigger hält updated_at zuverlässig aktuell und schreibt an einer zentralen Stelle einen Audit-Eintrag. Der Anwendungscode, der das UPDATE absetzt, muss von beidem nichts wissen.

Weiter geht's: JSON-Unterstützung

Trigger kümmern sich um die Automatisierung rund um Zeilenereignisse. Der nächste fortgeschrittene SQLite-Baustein dreht sich darum, was du innerhalb einer Zeile ablegen kannst — nämlich JSON. SQLite bringt einen kompletten Satz an JSON-Funktionen mit, mit denen du strukturierte Daten direkt in SQL abfragen und verändern kannst. Genau darum geht's auf der nächsten Seite.

Häufig gestellte Fragen

Was ist ein Trigger in SQLite?

Ein Trigger ist ein Stück SQL, das automatisch ausgeführt wird, sobald an einer Tabelle ein bestimmtes Ereignis passiert – also ein INSERT, UPDATE oder DELETE. Du legst ihn einmal mit CREATE TRIGGER an, danach kümmert sich SQLite selbst darum, ihn beim passenden Event auszulösen. So baust du Audit-Logs, hältst abgeleitete Spalten konsistent oder erzwingst Regeln, ohne dass die Anwendung daran denken muss.

Worin unterscheiden sich BEFORE-, AFTER- und INSTEAD OF-Trigger?

BEFORE läuft, bevor die Änderung an der Zeile geschrieben wird – ideal zum Validieren oder um Werte vorher noch anzupassen. AFTER greift erst, nachdem die Änderung erfolgt ist – passend für Logging oder das Synchronisieren anderer Tabellen. INSTEAD OF funktioniert ausschließlich auf Views und ersetzt die eigentliche Operation komplett – damit lässt sich eine View überhaupt erst beschreibbar machen.

Wie greife ich im Trigger auf die betroffene Zeile zu?

Mit NEW.spalte kommst du an die neue Zeile bei INSERT und UPDATE, mit OLD.spalte an den bisherigen Stand bei UPDATE und DELETE. INSERT-Trigger sehen nur NEW, DELETE-Trigger nur OLD, und bei UPDATE hast du beides zur Verfügung. Die Referenzen gelten immer für die Zeile, die der Trigger gerade verarbeitet.

Wie liste oder lösche ich Trigger in SQLite?

Trigger stehen in sqlite_master – mit SELECT name, tbl_name FROM sqlite_master WHERE type = 'trigger'; siehst du alle auf einen Blick. Zum Entfernen reicht DROP TRIGGER trigger_name; oder DROP TRIGGER IF EXISTS trigger_name;, falls du dir nicht sicher bist, ob er überhaupt existiert. Wird die zugehörige Tabelle gelöscht, verschwinden ihre Trigger automatisch mit.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S