SQL триггеры: примеры для новичка

Триггеры в SQL — это не первая тема для новичка. Но запрос sql триггеры примеры обычно появляется в понятной ситуации: хочется, чтобы база сама реагировала на изменение данных

Например, у заказа поменялся статус. Был pending, стал paid. Хорошо бы автоматически записать это в журнал изменений: когда поменяли, какой был старый статус, какой стал новый. Можно делать это в коде приложения, а можно на уровне базы через trigger

В этом уроке разберем триггер как практический механизм, а не как страшное слово из администрирования

Что получится в конце

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

CREATE TRIGGER log_order_status_change
AFTER UPDATE OF status ON orders
FOR EACH ROW
WHEN OLD.status <> NEW.status
BEGIN
  INSERT INTO order_audit (order_id, old_status, new_status, changed_at)
  VALUES (OLD.id, OLD.status, NEW.status, CURRENT_TIMESTAMP);
END;

Пример ориентирован на SQLite-подобный синтаксис, потому что его удобно запускать в online-редакторах. В SQL Server, MySQL и PostgreSQL синтаксис триггеров отличается, но сама идея такая же: событие в таблице запускает заранее описанное действие

Когда триггеры вообще нужны

Триггер — это код в базе данных, который срабатывает при событии:

  • строку добавили;
  • строку обновили;
  • строку удалили.

Типичные задачи:

  • записать историю изменений;
  • автоматически заполнить техническое поле;
  • проверить дополнительное правило;
  • синхронизировать связанную таблицу;
  • запретить опасное изменение.

Но триггер не стоит использовать для всего подряд. Если действие относится к бизнес-логике приложения и должно быть явно видно в коде, часто лучше оставить его в приложении. Триггеры полезны там, где правило должно сработать независимо от того, кто изменил данные: приложение, админ, скрипт или импорт

Готовим таблицу заказов

CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  customer TEXT,
  amount INTEGER,
  status TEXT
);

Добавим данные:

INSERT INTO orders (id, customer, amount, status) VALUES
(1, 'Анна', 7900, 'pending'),
(2, 'Игорь', 4500, 'paid'),
(3, 'Мария', 1200, 'pending');

Проверим:

SELECT *
FROM orders;

Создаем таблицу аудита

Аудит — это журнал. В него будем писать изменения статуса:

CREATE TABLE order_audit (
  id INTEGER PRIMARY KEY,
  order_id INTEGER,
  old_status TEXT,
  new_status TEXT,
  changed_at TEXT
);

Здесь:

  • order_id — какой заказ изменился;
  • old_status — старый статус;
  • new_status — новый статус;
  • changed_at — когда произошло изменение.

Создаем trigger

CREATE TRIGGER log_order_status_change
AFTER UPDATE OF status ON orders
FOR EACH ROW
WHEN OLD.status <> NEW.status
BEGIN
  INSERT INTO order_audit (order_id, old_status, new_status, changed_at)
  VALUES (OLD.id, OLD.status, NEW.status, CURRENT_TIMESTAMP);
END;

Разберем по строкам:

  • CREATE TRIGGER log_order_status_change — создаем триггер с именем;
  • AFTER UPDATE OF status ON orders — срабатывает после обновления колонки status в таблице orders;
  • FOR EACH ROW — срабатывает для каждой измененной строки;
  • WHEN OLD.status <> NEW.status — только если статус действительно изменился;
  • OLD.status — значение до изменения;
  • NEW.status — значение после изменения;
  • INSERT INTO order_audit — запись в журнал.

Проверяем работу

Обновим заказ Анны:

UPDATE orders
SET status = 'paid'
WHERE id = 1;

Теперь посмотрим журнал:

SELECT *
FROM order_audit;

Ожидаем примерно такую строку:

Мы не вставляли эту строку вручную. Ее создал триггер

Почему WHEN важен

Попробуем обновить сумму, не меняя статус:

UPDATE orders
SET amount = 8000
WHERE id = 1;

Триггер не должен добавить запись, потому что он срабатывает только на UPDATE OF status

Теперь обновим статус тем же значением:

UPDATE orders
SET status = 'paid'
WHERE id = 1;

Из-за условия:

WHEN OLD.status <> NEW.status

запись тоже не должна появиться. Мы защищаем журнал от мусора

BEFORE и AFTER

Триггеры часто бывают:

  • BEFORE — до изменения;
  • AFTER — после изменения.

Для аудита обычно удобно AFTER: изменение уже произошло, и мы логируем факт. Для проверки или нормализации значения иногда используют BEFORE

Например, если нужно запретить отрицательную сумму заказа, это уже ближе к проверке. Но во многих базах для таких правил лучше использовать CHECK-ограничения, а не триггер:

CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  amount INTEGER CHECK (amount >= 0)
);

Не надо решать триггером то, что проще и надежнее решается ограничением таблицы

Как это выглядит в разных базах

Идея триггера общая, но синтаксис отличается

В SQLite мы можем использовать OLD и NEW прямо внутри триггера

В MySQL тоже есть OLD и NEW, но для создания многострочного триггера в консоли часто меняют delimiter

В SQL Server логика другая: там обычно используют псевдотаблицы inserted и deleted, потому что один UPDATE может изменить сразу много строк

Поэтому публикационный урок должен честно говорить: пример показывает принцип. Если читатель работает в конкретной базе, нужно адаптировать синтаксис под нее

Частые ошибки

Использовать триггер вместо понятного кода

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

Не учитывать массовые UPDATE

Запрос может изменить не одну строку, а сотни:

UPDATE orders
SET status = 'paid'
WHERE status = 'pending';

Триггер сработает для каждой строки. Это может быть нормально, но нужно понимать нагрузку и объем записей в журнале

Забыть, что триггер работает для всех

Триггер сработает не только из приложения, но и при ручном SQL-запросе, импорте или административном скрипте. Это плюс для контроля, но минус, если команда об этом не знает

Создать бесконечную цепочку

Если триггер обновляет таблицу, которая запускает другой триггер, можно получить сложную цепочку. В учебных примерах лучше держать действие простым: изменение в orders пишет строку в order_audit

Когда триггеры не нужны

Не стоит начинать с триггеров, если ты еще не уверен в INSERT, UPDATE, DELETE, JOIN и транзакциях. Триггер прячет действие: ты выполняешь один запрос, а база делает еще что-то за кадром

Плохие кандидаты для триггера:

  • отправка писем;
  • сложные бизнес-процессы;
  • логика, которая часто меняется;
  • действия, которые требуют внешних API;
  • правила, которые проще выразить через constraints.

Хорошие кандидаты:

  • audit log;
  • автоматическая техническая отметка;
  • защита инварианта данных;
  • история изменения важного статуса.

Мини-задания

  1. Добавь заказ со статусом pending.
  2. Измени статус на paid.
  3. Проверь таблицу order_audit.
  4. Измени сумму заказа и убедись, что журнал не пополнился.
  5. Измени статус paid на cancelled и проверь вторую запись аудита.

Проверочный запрос:

SELECT order_id, old_status, new_status, changed_at
FROM order_audit
ORDER BY id;

Ответы на эти вопросы могут быть для вас полезными

Что такое триггер в SQL?

Триггер — это действие в базе данных, которое автоматически срабатывает при событии в таблице: добавлении, обновлении или удалении строки

Зачем нужны SQL триггеры?

Чаще всего для аудита, автоматических технических действий и правил, которые должны сработать независимо от приложения

Чем триггер отличается от обычного запроса?

Обычный запрос ты запускаешь явно. Триггер запускается автоматически, когда происходит заданное событие

Триггеры одинаковые в MySQL, SQL Server и SQLite?

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

Опасно ли использовать триггеры?

Опасно использовать их без понимания. Хороший триггер решает узкую задачу и легко проверяется. Плохой триггер прячет сложную бизнес-логику и делает систему непредсказуемой

Что почитать дальше по SQL

Если вы собираете тему по шагам, рядом лучше открыть:

Оцените статью
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x