Что такое triggers в PostgreSQL?

MiddlePostgreSQL · Backend·Обновлено 25 июля 2026
Коротко
Триггер — это функция, автоматически вызываемая PostgreSQL при выполнении определённых событий (INSERT, UPDATE, DELETE) на таблице или представлении. Триггеры используются для реализации бизнес-логики на уровне базы данных, аудита и поддержания целостности данных.

Что такое триггер в PostgreSQL

Триггер — это объект базы данных, связывающий событие (INSERT, UPDATE, DELETE, TRUNCATE) с функцией, которая автоматически выполняется при наступлении этого события. Триггер не вызывается явно — он срабатывает как реакция на изменение данных.

Каждый триггер состоит из двух частей:

  • Триггерная функция — хранимая процедура, написанная на PL/pgSQL (или другом языке), возвращающая тип TRIGGER.
  • Определение триггера — привязывает функцию к таблице, событию и моменту срабатывания.

Моменты срабатывания

  • BEFORE — триггер вызывается до применения изменения. Функция может модифицировать строку (NEW) или отменить операцию, вернув NULL.
  • AFTER — вызывается после успешного применения изменения. Используется для логирования, отправки уведомлений.
  • INSTEAD OF — применяется только к представлениям (VIEW), заменяет стандартную операцию.

Уровни срабатывания

  • FOR EACH ROW — функция вызывается отдельно для каждой затронутой строки. Внутри доступны OLD (прежнее значение) и NEW (новое значение).
  • FOR EACH STATEMENT — функция вызывается один раз на весь оператор, независимо от числа затронутых строк.

Специальные переменные в триггерной функции

  • NEW — новая строка (доступна при INSERT и UPDATE).
  • OLD — старая строка (доступна при UPDATE и DELETE).
  • TG_OP — строка с названием операции: 'INSERT', 'UPDATE', 'DELETE'.
  • TG_TABLE_NAME — имя таблицы, на которой сработал триггер.

Пример: аудит изменений

-- Создаём таблицу аудита
CREATE TABLE users_audit (
    id SERIAL PRIMARY KEY,
    user_id INT,
    old_email TEXT,
    new_email TEXT,
    changed_at TIMESTAMPTZ DEFAULT now()
);

-- Триггерная функция
CREATE OR REPLACE FUNCTION log_user_email_change()
RETURNS TRIGGER AS $$
BEGIN
    -- Пишем запись только если email изменился
    IF OLD.email IS DISTINCT FROM NEW.email THEN
        INSERT INTO users_audit(user_id, old_email, new_email)
        VALUES (OLD.id, OLD.email, NEW.email);
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Привязываем функцию к таблице
CREATE TRIGGER trg_user_email_audit
AFTER UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION log_user_email_change();

Пример: автоматическое заполнение поля

-- Триггер устанавливает updated_at перед каждым обновлением
CREATE OR REPLACE FUNCTION set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = now();
    RETURN NEW; -- возвращаем изменённую строку
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_set_updated_at
BEFORE UPDATE ON products
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();

Управление триггерами

-- Временно отключить триггер
ALTER TABLE users DISABLE TRIGGER trg_user_email_audit;

-- Включить обратно
ALTER TABLE users ENABLE TRIGGER trg_user_email_audit;

-- Удалить триггер
DROP TRIGGER trg_user_email_audit ON users;

Когда использовать триггеры

Триггеры подходят для: автоматического аудита, поддержания денормализованных агрегатов, валидации сложных ограничений, обновления updated_at. Их следует избегать для сложной бизнес-логики, которую лучше реализовать на уровне приложения — это упрощает отладку и тестирование.

Что хочет услышать интервьюер

Понимание разницы между BEFORE и AFTER триггерами и когда применять каждый из них

Знание переменных NEW и OLD и их доступности в зависимости от типа операции

Понимание разницы между FOR EACH ROW и FOR EACH STATEMENT

Умение написать триггерную функцию на PL/pgSQL и привязать её к таблице

Осознание побочных эффектов: влияние на производительность, сложность отладки, скрытая логика

Пример: AFTER-триггер: аудит изменения email

-- Триггерная функция для автоматического аудита email
CREATE OR REPLACE FUNCTION log_user_email_change()
RETURNS TRIGGER AS $$
BEGIN
    IF OLD.email IS DISTINCT FROM NEW.email THEN
        INSERT INTO users_audit(user_id, old_email, new_email)
        VALUES (OLD.id, OLD.email, NEW.email);
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_user_email_audit
AFTER UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION log_user_email_change();

Пример: BEFORE-триггер: автообновление updated_at

-- BEFORE-триггер автоматически проставляет updated_at
CREATE OR REPLACE FUNCTION set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = now();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_set_updated_at
BEFORE UPDATE ON products
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();

Типичные ошибки

Путают момент срабатывания BEFORE и AFTER: возвращают NEW в AFTER-триггере, хотя это уже не влияет на строку

Забывают, что в DELETE-триггере переменная NEW недоступна — обращение к ней вызывает ошибку

Не учитывают рекурсивный вызов: триггер на UPDATE изнутри снова модифицирует таблицу и запускает сам себя

Используют FOR EACH STATEMENT, ожидая доступа к NEW/OLD, хотя на уровне оператора эти переменные недоступны

Перегружают триггеры бизнес-логикой, делая систему непредсказуемой и трудной для тестирования

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее