Найти тему
Уйти в АйТи

Триггеры в PostgreSQL. Примеры, плюсы и минусы

Здравствуйте, уважаемые подписчики и гости канала!

пример настройки триггера на обновление даты модификации при любом affect-е строки (даже если она реально не обновилась)
пример настройки триггера на обновление даты модификации при любом affect-е строки (даже если она реально не обновилась)

Что такое триггеры и триггерные функции в Postgres

Простыми словами - это hook или middleware механизм как в любом современном веб-фреймворке, который позволяет привязаться к событиям базы данных.

Может быть вы ищете статью про обычные функции в БД, если так, то переходите по ссылке.

Обычно это что-то, на добавление, изменение или удаление строк. И при наступлении такого события вы хотите до его реального выполнения (например BEFORE UPDATE) или после (AFTER UPDATE) что-то сделать. Тут как раз вы делаете триггерную функцию на sql, plpgsql, plpythonu или любом другом языке для хранимых процедур в Postgres, лишь бы этот соответствующее расширение (в postgres это называется EXTENSION) у вас было установлено.

Минутка рекламы - я вижу, что эта статья пользуется очень большой популярностью и полезна многим. Уверен, что мой ТГ канал тоже будет вам полезен, подписывайтесь и получайте интересные заметки, заодно таким образом поможете мне с его развитием - Уйти в IT! в Телеграм

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

Я сейчас не буду вдаваться в мульти-инстанс БД и случай с отказом мастера с потерей данных в нагрузку с плохой асинхронной репликацией на слейвы. Это отдельная тема и там вообще все сложно - много точек отказа.

"100%" гарантия выполнения

А в остальном если кратко, то триггеры нужны всегда, когда вам действительно нужна 100% гарантия изменения данных в соседних колонках при автоматическом пересчете чего-то или. например, для гарантии складывания лога в отдельную таблицу. Почему? Просто так повелось, что сервера приложений - штука ненадежная и там просто масса вариантов того в какой момент оно все отлетит. А в БД обычно все четко - mvcc, изоляция и все дела.

С недавнего времени я записываю видеокурс по postgresql. И вот как раз видео на тему триггеров

Подборка видео всех видео по PG на Дзене тут для тех, кто по разным причинам не может смотреть на YT - https://dzen.ru/suite/37b67ffa-176d-493a-b1a8-4762f79e3753

Пример простейшего триггера

Сперва создаем триггерную функцию:

CREATE OR REPLACE FUNCTION public.update_modification_time()
RETURNS trigger AS
$BODY$
BEGIN
NEW.modification_time = now();
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

Добавляем колонку в БД

ALTER TABLE mytbl ADD COLUMN modification_time timestamp NOT NULL DEFAULT NOW();

И добавляем триггер на UPDATE строки

CREATE TRIGGER update_modification_time_mytbl
BEFORE UPDATE
ON mytbl
FOR EACH ROW
EXECUTE PROCEDURE public.update_modification_time();

Тут есть несколько важных моментов:

1. BEFORE UPDATE тут не просто так, для того, чтобы не делать лишний update той же строки. Конструкция NEW.modification_time = now(); в триггерной функции как раз добавляет в объект новых данных NEW значение для поля modification_time

2. Такой код делает обновление при любом update-е. Даже таком UPDATE mytbl SET id=id. Т.е. postgres-у будет не важно реально ли поменялись данные или нет. Если вам прям важно что-то проверять, то читайте ниже

3. Код работает на UPDATE любой колонки

4. Код триггера для INSERT, UPDATE, DELETE принципиально одинаковый, кроме того, что в параметре TG_OP при INSERT не будет OLD, при DELETE не будет NEW, а при UPDATE будет всё.

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

Приведу реальный пример из работающего кода.

CREATE TRIGGER "updateAccountTime"
BEFORE UPDATE OF state ON accounts
FOR EACH ROW WHEN ((new.state <> old.state))
EXECUTE PROCEDURE "updateAccountTime"();

Задача - обновлять поле с временем модификации аккаунта только тогда, когда в UPDATE запросе обновляется поле state (BEFORE UPDATE OF state), при этом старый state не должен быть равен новому ( FOR EACH ROW WHEN ((new.state <> old.state)) )

Важное замечание - у нас там не null-ов, а вы помните, что null == null и null <> null вернут null, а не true или false и поля с null надо сравнивать иначе&

Оптимизации

Хорошо Не хорошо тратить деньги инвесторов на 100500 серверов приложений просто так. Порой встает вопрос - сделать быстро и оптимально что-то прямо в БД, будь-то сложный сложный запрос, триггер или еще что-то или переписать половину кода. Обычно выбор очевиден. 😊 Да и в случае с триггером какие-то пересчеты можно делать сразу мгновенно, а не через брокер очередей или не к ночи будет помянут ежечасный cron. Минусы тут в том, что БД не отпустит ваш запрос пока триггеры не доделает, так что тут ухо в остро.

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

Могло показаться обратное, но лично я - не сторонник упаковки в БД всего, чего только можно. По мне лучшее место для бизнес логики - это все же код приложения - там и git и деплой проще, понятнее и без потенциальных блокировок из-за патчей бд. Однако разные ситуации бывают. Я выше писал когда без триггеров никак.

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

Литература

Ребята из Postgres PRO как обычно очень хорошо написали или перевели официальную доку

Кстати, если ты не знаешь что такое и для чего существуют оконные функции, то читай другую мою статью про оконные функции в Postgres.

---

А на этом всё, спасибо за внимание!

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

Кроме этого:

Подписывайтесь в Telegram: https://t.me/lets_goto_it