ПрограммированиеDatabase Engineer / DBA

Поясните принцип работы триггеров в SQL, их плюсы и минусы, а также типичные сценарии использования. Приведите пример реального триггера.

Проходите собеседования с ИИ помощником Hintsage

Ответ

Триггеры — специальные объекты базы данных, которые автоматически запускают выполнение определённого кода (обычно на SQL или языках процедурных расширений, например PL/pgSQL для PostgreSQL) при наступлении события: INSERT, UPDATE или DELETE на таблице.

Преимущества:

  • Централизованное применение бизнес-логики и валидации данных.
  • Автоматизация аудита (например, история изменений).

Недостатки:

  • Скрытое выполнение — не всегда очевидно, что триггер исполнился.
  • Снижение производительности при частых изменениях таблиц.
  • Сложности отладки и дебага, риск зацикливания (рекурсивные триггеры).
  • Миграции данных становятся сложнее.

Когда применять:

  • Аудит данных.
  • Автоматическое создание связанных записей.
  • Реализация контроля ограничений, невозможных стандартными средствами.

Пример триггера (PostgreSQL):

CREATE TABLE employee_audit ( id SERIAL PRIMARY KEY, employee_id INT, old_salary NUMERIC, new_salary NUMERIC, changed_at TIMESTAMP ); CREATE OR REPLACE FUNCTION audit_salary() RETURNS TRIGGER AS $$ BEGIN IF NEW.salary <> OLD.salary THEN INSERT INTO employee_audit(employee_id, old_salary, new_salary, changed_at) VALUES (OLD.id, OLD.salary, NEW.salary, NOW()); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_salary_update AFTER UPDATE ON employees FOR EACH ROW EXECUTE FUNCTION audit_salary();

Вопрос с подвохом

Можно ли внутри триггера обновить ту же таблицу, на которую он установлен? Что произойдет?

Ответ:
Если триггер настроен на событие, скажем, UPDATE таблицы и внутри себя выполняет ещё один UPDATE этой же таблицы, это приведёт к возникновению бесконечной рекурсии (зацикливанию) и аварийному завершению команды или ошибке превышения глубины триггеров. Поэтому всегда контролируйте, чтобы не возникала рекурсия, или используйте опции, разрешающие/запрещающие рекурсивные вызовы.

Пример:

-- Такой конструкцией можно зациклить триггер: CREATE OR REPLACE FUNCTION recursive_update() RETURNS TRIGGER AS $$ BEGIN UPDATE employees SET salary = salary * 1.01 WHERE id = NEW.id; -- сработает снова RETURN NEW; END; $$ LANGUAGE plpgsql;

История

В бухгалтерской CRM внедрили триггер для автоматической записи операций в лог при изменении таблицы транзакций. Под высокой нагрузкой сервис стал работать медленнее, а анализ показал: триггер тратит много времени на вставку в лог и не был оптимизирован (например, не писал только существенные события).


История

В проекте управления инвентаризацией использовался BEFORE INSERT триггер для расчёта ID позиции. Неправильная логика ID провоцировала дублирование данных, причём ошибка долго оставалась незамеченной, потому что работала "за кадром".


История

В HR-платформе триггер делал массовое обновление записей в родительской и дочерней таблицах при каждом INSERT. Из-за ошибок в логике триггера возникла рекурсия, что полностью блокировало все операции с таблицами. Пришлось отключить триггер и сделать сложную rollback-митацию для разблокирования таблиц.