0Pricing
SQL Academy · Урок

Аудит таблиц с помощью триггеров

Создавайте журнал аудита с помощью триггеров AFTER INSERT/UPDATE/DELETE, записывающих данные в таблицу audit_log

«Аудит таблиц с помощью триггеров» — бесплатный урок SQL Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения SQL Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс SQL Academy содержит 4 уроков всего.

Зачем нужен аудит?

Журналы аудита отвечают на вопросы «кто, что и когда изменил». Они необходимы для соответствия требованиям (HIPAA, SOX, расследований по праву GDPR на удаление данных) и операционной криминалистики.

Таблица аудита

Одна центральная таблица фиксирует каждое изменение:

CREATE TABLE audit_log (
  id BIGSERIAL PRIMARY KEY,
  ts TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  user_name TEXT NOT NULL DEFAULT CURRENT_USER,
  table_name TEXT NOT NULL,
  action TEXT NOT NULL,             -- INSERT, UPDATE, DELETE
  row_id TEXT,
  old_data JSONB,
  new_data JSONB
);

Функция триггера аудита

Одна функция, которую можно повторно использовать для многих таблиц:

CREATE OR REPLACE FUNCTION audit_row()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO audit_log (table_name, action, row_id, old_data, new_data)
  VALUES (
    TG_TABLE_NAME,
    TG_OP,
    COALESCE(NEW.id::TEXT, OLD.id::TEXT),
    CASE WHEN TG_OP IN ('UPDATE','DELETE') THEN to_jsonb(OLD) END,
    CASE WHEN TG_OP IN ('INSERT','UPDATE') THEN to_jsonb(NEW) END
  );
  RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql;

Подключение к таблицам

Создайте триггер для AFTER INSERT/UPDATE/DELETE:

CREATE TRIGGER trg_audit_users
AFTER INSERT OR UPDATE OR DELETE ON users
FOR EACH ROW EXECUTE FUNCTION audit_row();

to_jsonb(NEW)

Приём to_jsonb позволяет обобщённо сохранить всю строку — без кода для каждого столбца. Он работает с любой таблицей, содержащей столбец id.

Отслеживание инициатора

Если приложение работает от имени одного пользователя БД, CURRENT_USER недостаточно. Передавайте пользователя приложения через GUC сеанса:

-- App sets:
SET LOCAL app.user_id = '42';

-- Trigger reads:
INSERT INTO audit_log (... actor_id ...) VALUES (..., current_setting('app.user_id', true)::BIGINT);

Аудит определённых столбцов

Используйте условие WHEN триггера для выборочного аудита:

CREATE TRIGGER trg_audit_role_change
AFTER UPDATE OF role ON users
FOR EACH ROW WHEN (OLD.role IS DISTINCT FROM NEW.role)
EXECUTE FUNCTION audit_role_change();

Таблицы аудита для отдельных таблиц

Альтернатива: одна таблица аудита для каждой реальной таблицы (например, users_audit) с той же схемой и метаданными аудита. Такие таблицы проще запрашивать, но для их сопровождения требуется больше DDL.

Аудит только изменений

Сохраняйте только изменённые столбцы:

INSERT INTO audit_log (table_name, action, changes)
VALUES (
  TG_TABLE_NAME, TG_OP,
  (SELECT jsonb_object_agg(key, value)
   FROM jsonb_each(to_jsonb(NEW))
   WHERE NEW.* IS DISTINCT FROM OLD.* AND to_jsonb(NEW)->key IS DISTINCT FROM to_jsonb(OLD)->key)
);

Производительность таблицы аудита

В загруженных системах таблица аудита быстро растёт. Выполняйте секционирование по времени:

CREATE TABLE audit_log (...) PARTITION BY RANGE (ts);
CREATE TABLE audit_log_2024_q1 PARTITION OF audit_log
  FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');

Компромисс производительности

Теперь каждая операция INSERT/UPDATE/DELETE записывает вторую строку. Для таблиц с очень высокой интенсивностью записи это может удвоить IO. Проверьте, отслеживайте и решите, оправдан ли такой компромисс.

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

Если Вам нужна интеграция с шиной событий, ставьте события в очередь с помощью NOTIFY или LISTEN либо используйте логическую репликацию или инструменты CDC (Debezium) вместо триггеров.

Итоги

Триггеры аудита — самый простой способ сохранять историю.

  • Одна универсальная функция с to_jsonb(NEW/OLD)
  • Подключайте её к каждой таблице, подлежащей аудиту
  • Секционируйте таблицы аудита по времени
  • Явно отслеживайте инициатора приложения через GUC

Быстрая проверка

Как внутри функции триггера аудита обобщённо сохранить новую строку в формате JSONB?

Часто задаваемые вопросы

Урок «Аудит таблиц с помощью триггеров» бесплатный?

Да — полный текст урока «Аудит таблиц с помощью триггеров» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс SQL Academy, подпишись на CoddyKit PRO. Курс SQL Academy содержит 4 уроков всего.

Чему я научусь в уроке «Аудит таблиц с помощью триггеров»?

Создавайте журнал аудита с помощью триггеров AFTER INSERT/UPDATE/DELETE, записывающих данные в таблицу audit_log Ты практикуешь SQL Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать SQL Academy?

Предыдущий опыт не требуется. SQL Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Аудит таблиц с помощью триггеров»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке SQL Academy?

Да. Каждый урок SQL Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Устройство триггеров: BEFORE/AFTER, FOR EACH ROW
  2. Основы функций PL/pgSQL
  3. Блоки DO и анонимный код
  4. Аудит таблиц с помощью триггеров
← Назад к SQL Academy