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:
BEFOREdziała przed zmianą,AFTERpo niej,INSTEAD OFją zastępuje (tylko widoki). - Zdarzenie: która operacja uruchamia wyzwalacz.
UPDATE OF col1, col2zawęż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
BEGINaEND. 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 wyzwalaczachINSERTiUPDATE.OLD: istniejący wiersz. Dostępny w wyzwalaczachUPDATEiDELETE.
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.