Ответ
Да, приходилось. Триггеры — это объекты базы данных, которые автоматически выполняют заданный код (обычно на SQL или PL/pgSQL) в ответ на определённое событие (INSERT, UPDATE, DELETE) в связанной таблице.
Типичный пример использования — аудит изменений:
-- Триггер для логирования изменения цены товара в PostgreSQL
CREATE OR REPLACE FUNCTION log_price_change()
RETURNS TRIGGER AS $$
BEGIN
IF OLD.price <> NEW.price THEN
INSERT INTO product_audit_log (
product_id,
old_price,
new_price,
changed_at,
changed_by
) VALUES (
NEW.id,
OLD.price,
NEW.price,
NOW(),
CURRENT_USER
);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER tr_product_price_audit
AFTER UPDATE ON products
FOR EACH ROW
EXECUTE FUNCTION log_price_change();
Ключевые моменты и best practices:
- Транзакционность: Триггер выполняется в контексте той же транзакции, что и вызвавшее его событие. Ошибка в триггере приведёт к откату всей операции.
- Производительность: Триггеры добавляют накладные расходы на каждую операцию. Слишком сложная логика или триггеры на часто изменяемых таблицах могут стать узким местом.
- Сложность отладки: Поскольку выполнение неявное, поиск причины неожиданного поведения данных может усложниться.
- Рекурсия: Необходимо контролировать, чтобы действия триггера не вызывали его повторное срабатывание (прямая или косвенная рекурсия). В некоторых СУБД есть настройки (
RECURSIVE_TRIGGERS). - Альтернативы: Для сложной бизнес-логики иногда предпочтительнее использовать хранимые процедуры как единственную точку входа для модификации данных или обрабатывать события на уровне приложения (например, через паттерн «Единый источник истины»).
Ответ 18+ 🔞
А, триггеры, да? Ну, приходилось, куда ж деваться. Это такие штуки в базе, которые сами срабатывают, когда ты что-то делаешь с таблицей — вставил, обновил, удалил. Как будто подвел мину под данные, и она сама бабахает твоим кодом.
Вот классический пример, чтобы следить, кто цену на товары меняет:
-- Триггер для логирования изменения цены товара в PostgreSQL
CREATE OR REPLACE FUNCTION log_price_change()
RETURNS TRIGGER AS $$
BEGIN
IF OLD.price <> NEW.price THEN
INSERT INTO product_audit_log (
product_id,
old_price,
new_price,
changed_at,
changed_by
) VALUES (
NEW.id,
OLD.price,
NEW.price,
NOW(),
CURRENT_USER
);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER tr_product_price_audit
AFTER UPDATE ON products
FOR EACH ROW
EXECUTE FUNCTION log_price_change();
А теперь, блядь, самое важное, чтобы не обжечься:
- Транзакция одна на всех: Триггер живёт и умирает вместе с операцией, которая его вызвала. Если в нём косяк — вся твоя основная вставка или обновление нахуй откатятся. Красота, да?
- Производительность ебёт: Это же не магия, а дополнительный код на каждую операцию. Навешаешь таких монстров на таблицу, которую тысяча запросов в секунду долбит, и потом будешь чесать репу, откуда лаги. Тяжёлую логику туда пихать — самоубийство.
- Отладка — пиздец: Потому что всё происходит неявно. Данные изменились как-то странно, а кто виноват? А хрен пойми! Где-то в триггере, который ты уже забыл, как звали, сидит баг и потихоньку всё проёбывает.
- Рекурсия, мать её: Самый эпик фейл — когда триггер срабатывает и своими действиями вызывает сам себя опять. Или через другую таблицу. И пошла цепная реакция, пока база не ляжет с ошибкой. В некоторых базах есть защита, но надеяться на неё — играть в рулетку.
- Может, не надо?: Для серьёзной бизнес-логики иногда лучше сделать так, чтобы данные менялись только через одну дверь — через какую-нибудь хранимую процедуру. Или вообще в коде приложения это обрабатывать. А то триггеры — они как минное поле: один не там поставил, и всем потом ебаться.