Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як працюють тригери в PostgreSQL і які в них підводні камені?

Тригер - функція, яку 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-коді, тестується звичайно й може ставити завдання в чергу.

Правило: інваріанти даних - у базі, побічні ефекти (листи, кеш, черги) - у застосунку.

Докладніше в документації: PL/pgSQL: тригерні функції

Схожі питання