Приходилось ли писать триггеры в базе данных?

Ответ

Да, приходилось. Триггеры — это объекты базы данных, которые автоматически выполняют заданный код (обычно на 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();

А теперь, блядь, самое важное, чтобы не обжечься:

  • Транзакция одна на всех: Триггер живёт и умирает вместе с операцией, которая его вызвала. Если в нём косяк — вся твоя основная вставка или обновление нахуй откатятся. Красота, да?
  • Производительность ебёт: Это же не магия, а дополнительный код на каждую операцию. Навешаешь таких монстров на таблицу, которую тысяча запросов в секунду долбит, и потом будешь чесать репу, откуда лаги. Тяжёлую логику туда пихать — самоубийство.
  • Отладка — пиздец: Потому что всё происходит неявно. Данные изменились как-то странно, а кто виноват? А хрен пойми! Где-то в триггере, который ты уже забыл, как звали, сидит баг и потихоньку всё проёбывает.
  • Рекурсия, мать её: Самый эпик фейл — когда триггер срабатывает и своими действиями вызывает сам себя опять. Или через другую таблицу. И пошла цепная реакция, пока база не ляжет с ошибкой. В некоторых базах есть защита, но надеяться на неё — играть в рулетку.
  • Может, не надо?: Для серьёзной бизнес-логики иногда лучше сделать так, чтобы данные менялись только через одну дверь — через какую-нибудь хранимую процедуру. Или вообще в коде приложения это обрабатывать. А то триггеры — они как минное поле: один не там поставил, и всем потом ебаться.