Тригер - функція, яку PostgreSQL викликає автоматично при INSERT, UPDATE, DELETE чи TRUNCATE.
CREATE FUNCTION set_updated_at() RETURNS trigger
LANGUAGE plpgsql AS $$
BEGIN
NEW.updated_at := now();
RETURN NEW;
END;
$$;
CREATE TRIGGER products_updated_at
BEFORE UPDATE ON products
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();
Варіанти тригерів:
| Що це | |
|---|---|
BEFORE |
до зміни: можна змінити NEW чи скасувати операцію (RETURN NULL) |
AFTER |
після зміни: аудит, оновлення інших таблиць |
FOR EACH ROW |
для кожного рядка, доступні OLD і NEW |
FOR EACH STATEMENT |
раз на оператор; з таблицями переходів REFERENCING NEW TABLE AS ... бачить усі змінені рядки |
WHEN (умова) |
тригер викликається лише при умові: WHEN (OLD.price IS DISTINCT FROM NEW.price) |
Типові застосування:
- журнал аудиту змін (хто, що, коли - навіть при змінах поза застосунком);
- денормалізовані лічильники й агрегати;
- підтримка
tsvectorдля повнотекстового пошуку; - перевірки, які не виразити обмеженням
CHECK.
Підводні камені:
- невидима логіка: розробник бачить
UPDATE products SET price = ...і не знає, що змінюються ще три таблиці. Тригери мають бути задокументовані й мати зрозумілі імена; - продуктивність: рядковий тригер виконується для кожного рядка - масовий
UPDATEмільйона рядків стає мільйоном викликів функції. Для масових змін - тригери рівня оператора з таблицями переходів; - каскади й рекурсія: тригер оновлює таблицю, на якій теж є тригер, - ланцюжки важко відстежити, а рекурсію - зупинити (
pg_trigger_depth()); - блокування й взаємоблокування: тригер, що оновлює рядок-лічильник, серіалізує всі паралельні вставки на цьому рядку;
- тести: у тестах Laravel з SQLite тригерів PostgreSQL немає - тести мають працювати на тій самій СУБД, що й продакшен.
Тригер чи подія Eloquent:
- тригер спрацьовує завжди, навіть при масовому
update(), сирому SQL чи зміні з іншого сервісу; - подія Eloquent - лише при збереженні моделі, зате вона в PHP-коді, тестується звичайно й може ставити завдання в чергу.
Правило: інваріанти даних - у базі, побічні ефекти (листи, кеш, черги) - у застосунку.