Menu

Wyzwalacze w SQLite: CREATE TRIGGER, BEFORE/AFTER i OLD/NEW

Jak działają wyzwalacze w SQLite: BEFORE i AFTER, INSTEAD OF na widokach, odwołania do wierszy OLD i NEW oraz sytuacje, w których trigger to właściwe narzędzie.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Wyzwalacz uruchamia SQL automatycznie

Wyzwalacz (trigger) to zapisany blok SQL, który odpala się za każdym razem, gdy na określonej tabeli zajdzie określone zdarzenie. Piszesz go raz. O to, „kiedy”, dba SQLite.

Tak to wygląda:

Nigdzie nie napisaliśmy jawnego INSERT do price_history. Zrobił to wyzwalacz. Każda przyszła zmiana ceny zostanie zapisana w ten sam sposób, niezależnie od tego, czy pochodzi z CLI, skryptu czy aplikacji.

Budowa CREATE TRIGGER

Przeczytaj składnię kawałek po kawałku:

CREATE TRIGGER trigger_name
{ BEFORE | AFTER | INSTEAD OF } { INSERT | UPDATE [ OF column_list ] | DELETE }
ON table_name
[ FOR EACH ROW ]
[ WHEN condition ]
BEGIN
    -- jedna lub więcej instrukcji
END;
  • Moment: BEFORE działa przed zmianą, AFTER po niej, INSTEAD OF ją zastępuje (tylko widoki).
  • Zdarzenie: która operacja uruchamia wyzwalacz. UPDATE OF col1, col2 zawęża aktualizacje do konkretnych kolumn.
  • Tabela: obserwowana tabela.
  • FOR EACH ROW: SQLite obsługuje tylko wyzwalacze na poziomie wiersza, więc to jest domyślne. Możesz to dopisać dla czytelności; niczego nie zmienia.
  • WHEN: opcjonalny warunek. Treść wyzwalacza wykonuje się tylko wtedy, gdy jest prawdziwy.
  • Treść: jedna lub więcej instrukcji między BEGIN a END. Każda musi kończyć się średnikiem.

To cała gramatyka. Większość prawdziwych wyzwalaczy ma od pięciu do dziesięciu linijek.

OLD i NEW: zmieniany wiersz

W treści wyzwalacza dwa pseudowiersze pozwalają zobaczyć dane:

  • NEW: nowy wiersz. Dostępny w wyzwalaczach INSERT i UPDATE.
  • OLD: istniejący wiersz. Dostępny w wyzwalaczach UPDATE i DELETE.

Wyzwalacz DELETE ma tylko OLD. Wyzwalacz INSERT ma tylko NEW. Wyzwalacz UPDATE ma oba.

Usunięty wiersz zniknął z accounts, ale jego dane zostały zapisane w deletions, zanim zniknął.

BEFORE: walidacja lub poprawka wiersza

Wyzwalacze BEFORE działają, zanim zmiana wiersza trafi na dysk. Przydają się do zgłoszenia błędu albo normalizacji danych:

Drugi INSERT zostaje przerwany, zanim jakikolwiek wiersz zostanie zapisany. RAISE(ABORT, '...') anuluje bieżącą instrukcję i wycofuje zmiany do jej początku; RAISE(FAIL, ...), RAISE(ROLLBACK, ...) i RAISE(IGNORE) dają dokładniejszą kontrolę nad tym, co się dzieje.

Do czystej walidacji danych lepiej używać ograniczeń CHECK: są deklaratywne, a optymalizator o nich wie. Po wyzwalacz BEFORE sięgnij, gdy reguła musi zajrzeć do innych tabel albo zrobić coś, czego CHECK nie wyrazi.

WHEN: wyzwalacze warunkowe

Klauzula WHEN filtruje, które zmiany wierszy faktycznie uruchamiają treść wyzwalacza. Jest obliczana dla każdego wiersza, po powiązaniu OLD i NEW:

Pierwsze zamówienie nie spełnia warunku. Dwa pozostałe tak. Bez klauzuli WHEN każde wstawienie trafiłoby do big_orders i filtrowanie musiałoby odbywać się przy odczycie.

INSTEAD OF: zapisywalny widok

Widoki domyślnie są tylko do odczytu. Wyzwalacz INSTEAD OF przechwytuje zapis do widoku i uruchamia zamiast niego twój SQL, zwykle tłumacząc go na zapisy do tabel źródłowych:

Aplikacja rozmawia z widokiem tak, jakby był tabelą. Wyzwalacz w tle dzieli dane na first_name i last_name.

Wyświetlanie i usuwanie wyzwalaczy

Wyzwalacze są zapisane w sqlite_master razem z tabelami i indeksami:

DROP TRIGGER IF EXISTS name; to bezpieczna forma. Usunięcie tabeli, do której należy wyzwalacz, automatycznie usuwa też wyzwalacz, więc nie musisz niczego sprzątać wcześniej.

Pułapki, o których warto wiedzieć

Kilka rzeczy zaskakuje ludzi za pierwszym razem:

  • Wyzwalacze odpalają się dla każdego wiersza, a nie dla każdej instrukcji. UPDATE, który zmienia 1000 wierszy, odpala wyzwalacz 1000 razy. Jeśli sama treść jest kosztowna, szybko się to sumuje.
  • Wyzwalacze działają w ramach otaczającej transakcji. Jeśli zewnętrzna instrukcja zostanie wycofana, zapisy wyzwalacza też. Zwykle o to chodzi, ale oznacza to, że wyzwalacz nie jest furtką typu „zaloguj to bez względu na wszystko”.
  • Wyzwalacze rekurencyjne są domyślnie wyłączone. Wyzwalacz, który modyfikuje tę samą tabelę, nie odpali się ponownie, dopóki nie ustawisz PRAGMA recursive_triggers = ON;. Zostaw to wyłączone, chyba że masz konkretny powód.
  • Zapisy z aplikacji mogą je ominąć tylko z pominięciem bazy. Dopóki każdy zapis przechodzi przez SQLite, wyzwalacz się uruchomi. ORM-y, które wysyłają paczki przez surowy SQL, też je uruchamiają.
  • Nie rozpraszaj logiki biznesowej po wielu wyzwalaczach. Nie widać ich w miejscu wywołania: ktoś, kto debuguje „skąd wziął się ten wiersz?”, musi przeszukiwać sqlite_master. Używaj ich do spraw przekrojowych (dzienniki audytu, kolumny pochodne, zapisywalne widoki), a resztę trzymaj w kodzie aplikacji.

Realistyczny przykład dziennika audytu

Łączymy wzorce: śledzimy każdą zmianę w tabeli posts:

Jeden wyzwalacz pilnuje, żeby updated_at było aktualne, i zapisuje wiersz audytu w jednym miejscu. Kod aplikacji, który wykonuje UPDATE, nie musi wiedzieć o żadnej z tych rzeczy.

Dalej: obsługa JSON

Wyzwalacze automatyzują reakcje na zdarzenia dotyczące wierszy. Kolejny element zaawansowanego SQLite to to, co możesz przechowywać wewnątrz wiersza, czyli JSON. SQLite ma pełny zestaw funkcji JSON do odpytywania i aktualizowania danych strukturalnych bez wychodzenia z SQL. O tym jest następna strona.

Najczęściej zadawane pytania

Czym jest wyzwalacz (trigger) w SQLite?

Wyzwalacz to blok SQL, który uruchamia się automatycznie, gdy na tabeli zajdzie określone zdarzenie: INSERT, UPDATE albo DELETE. Definiujesz go raz przez CREATE TRIGGER, a SQLite odpala go za ciebie przy każdym takim zdarzeniu. Tak prowadzi się dzienniki audytu, utrzymuje kolumny pochodne albo wymusza reguły bez polegania na tym, że aplikacja o nich pamięta.

Czym różnią się wyzwalacze BEFORE, AFTER i INSTEAD OF?

BEFORE działa przed zastosowaniem zmiany wiersza: przydaje się do walidacji albo poprawiania danych. AFTER działa, gdy zmiana już nastąpiła: przydaje się do logowania albo synchronizacji innych tabel. INSTEAD OF działa tylko na widokach i całkowicie zastępuje planowaną operację, dzięki czemu widok staje się zapisywalny.

Jak odwołać się do zmienianego wiersza wewnątrz wyzwalacza?

Użyj NEW.column dla nowego wiersza przy INSERT i UPDATE oraz OLD.column dla istniejącego wiersza przy UPDATE i DELETE. Wyzwalacze INSERT widzą tylko NEW, wyzwalacze DELETE tylko OLD, a wyzwalacze UPDATE oba. Te odwołania dotyczą wiersza, który jest właśnie przetwarzany.

Jak wyświetlić lub usunąć wyzwalacze w SQLite?

Wyzwalacze są zapisane w sqlite_master: SELECT name, tbl_name FROM sqlite_master WHERE type = 'trigger'; pokazuje wszystkie. Aby usunąć jeden, użyj DROP TRIGGER trigger_name; albo DROP TRIGGER IF EXISTS trigger_name;, jeśli nie masz pewności, że istnieje. Usunięcie tabeli usuwa też jej wyzwalacze.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ