Триггеры SQL
Триггер SQL — это хранимая процедура особого типа, которая автоматически выполняется (срабатывает) при наступлении определённого события в базе данных, связанного с изменением данных в таблице или представлении (вставка, обновление или удаление строк). Триггеры являются частью серверной логики СУБД и используются для обеспечения целостности данных, реализации сложных бизнес-правил, аудита изменений и каскадных операций, не зависящих от клиентского приложения.
История
Концепция триггеров была впервые реализована в коммерческих СУБД в конце 1980-х — начале 1990-х годов. Одной из первых систем, включивших триггеры, стала InterBase (1985). В 1986 году стандарт SQL-86 не предусматривал триггеров; они были добавлены в стандарт SQL:1999 (ISO/IEC 9075-2:1999) как часть объектно-реляционных расширений. До этого триггеры существовали как проприетарные расширения в Sybase (1987), Oracle (1992) и Microsoft SQL Server (1993). В PostgreSQL триггеры появились в версии 6.0 (1997). Стандарт SQL:2003 уточнил синтаксис и семантику триггеров, включая поддержку триггеров INSTEAD OF для представлений.
Классификация триггеров
По времени срабатывания относительно события
- BEFORE (ДО) — триггер выполняется до того, как операция модификации данных (INSERT, UPDATE, DELETE) будет применена к таблице. Позволяет проверять или изменять данные перед их записью, что даёт возможность отменить операцию или скорректировать значения.
- AFTER (ПОСЛЕ) — триггер выполняется после того, как операция модификации завершена. Используется для выполнения действий, которые должны произойти только после успешного изменения данных (например, обновление агрегированных таблиц, аудит).
- INSTEAD OF (ВМЕСТО) — триггер выполняется вместо самой операции. Применяется в основном для представлений (VIEW), которые не допускают прямых модификаций данных (например, представления на основе нескольких таблиц). Триггер INSTEAD OF перехватывает операцию и реализует собственную логику изменения базовых таблиц.
По уровню срабатывания
- Триггер уровня строки (ROW-level) — срабатывает один раз для каждой строки, затрагиваемой операцией. Например, при UPDATE, изменяющем 10 строк, триггер уровня строки выполнится 10 раз. Даёт доступ к значениям строки до и после изменения (через псевдозаписи OLD и NEW).
- Триггер уровня оператора (STATEMENT-level) — срабатывает один раз на всю операцию, независимо от количества затронутых строк. Используется для действий, которые не зависят от конкретных строк (например, проверка общего количества изменений или блокировка).
По типу события
- INSERT — срабатывает при добавлении новой строки.
- UPDATE — срабатывает при обновлении существующей строки.
- DELETE — срабатывает при удалении строки.
- TRUNCATE — в некоторых СУБД (например, PostgreSQL) триггер может срабатывать на TRUNCATE, хотя по стандарту это не операция модификации строк.
По области действия
- Триггеры на таблицах — наиболее распространённый тип.
- Триггеры на представлениях — обычно только INSTEAD OF.
- Триггеры на событиях базы данных (в некоторых СУБД, например, Oracle, PostgreSQL) — срабатывают при событиях, не связанных с модификацией данных: LOGON/LOGOFF, DDL-операции (CREATE, ALTER, DROP), ошибках сервера.
Устройство и синтаксис
Триггер состоит из трёх основных частей:
- Событие — определяет, при какой операции (INSERT, UPDATE, DELETE) и на каком объекте (таблица, представление) триггер должен срабатывать.
- Время срабатывания — BEFORE, AFTER или INSTEAD OF.
- Тело триггера — блок кода на процедурном языке СУБД (PL/SQL в Oracle, T-SQL в Microsoft SQL Server, PL/pgSQL в PostgreSQL, SQL/PSM в DB2), который выполняется при активации триггера.
Обобщённый синтаксис (по стандарту SQL:2003):
``sql CREATE TRIGGER trigger_name {BEFORE | AFTER | INSTEAD OF} {INSERT | UPDATE | DELETE} ON table_name [FOR EACH {ROW | STATEMENT}] [WHEN (condition)] BEGIN -- тело триггера END; ``
Внутри тела триггера доступны специальные переменные (псевдозаписи):
- OLD — содержит значения строки до изменения (для UPDATE и DELETE).
- NEW — содержит значения строки после изменения (для INSERT и UPDATE).
- OLD TABLE / NEW TABLE — в некоторых СУБД (Oracle, SQL Server) доступны таблицы переходов, содержащие все изменённые строки для уровня оператора.
Применение
Обеспечение целостности данных
Триггеры используются для реализации ограничений, которые невозможно или сложно задать через декларативные ограничения (PRIMARY KEY, FOREIGN KEY, CHECK, UNIQUE). Например:
- Проверка бизнес-правил, не сводящихся к простым условиям (например, «зарплата сотрудника не может превышать зарплату его руководителя»).
- Каскадное обновление или удаление связанных записей, если внешние ключи с CASCADE не подходят по логике.
- Защита от некорректных данных, которые могут быть вставлены через обход приложения (прямой SQL-доступ).
Аудит и логирование
Триггеры AFTER INSERT/UPDATE/DELETE могут автоматически записывать информацию об изменениях в специальную таблицу аудита: кто, когда, какие данные изменил, старые и новые значения. Это позволяет отслеживать действия пользователей без модификации кода приложения.
Поддержка материализованных представлений
В некоторых СУБД триггеры используются для автоматического обновления материализованных представлений при изменении базовых таблиц.
Реализация сложной бизнес-логики
Триггеры могут выполнять вычисления, обновлять связанные таблицы, вызывать хранимые процедуры, отправлять уведомления (например, через DBMS_SCHEDULER или pg_notify).
Защита от удаления или изменения критических данных
Триггеры BEFORE DELETE могут отменять операцию, если удаляемая строка соответствует определённым условиям (например, нельзя удалить пользователя с активными заказами).
Примеры
Пример 1. Триггер BEFORE INSERT для автоматической установки значения по умолчанию (PostgreSQL):
```sql CREATE OR REPLACE FUNCTION set_created_at() RETURNS TRIGGER AS $$ BEGIN NEW.created_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql;
CREATE TRIGGER trg_set_created_at BEFORE INSERT ON users FOR EACH ROW EXECUTE FUNCTION set_created_at(); ```
Пример 2. Триггер AFTER UPDATE для аудита (Oracle):
``sql CREATE OR REPLACE TRIGGER audit_salary_change AFTER UPDATE OF salary ON employees FOR EACH ROW BEGIN INSERT INTO salary_audit (employee_id, old_salary, new_salary, changed_by, change_date) VALUES (:OLD.employee_id, :OLD.salary, :NEW.salary, USER, SYSDATE); END; ``
Пример 3. Триггер INSTEAD OF на представлении (Microsoft SQL Server):
``sql CREATE TRIGGER trg_employee_details_insert ON EmployeeDetailsView INSTEAD OF INSERT AS BEGIN INSERT INTO Employees (Name, DepartmentID) SELECT i.Name, d.DepartmentID FROM inserted i JOIN Departments d ON i.DepartmentName = d.DepartmentName; END; ``
Критика и ограничения
- Производительность — триггеры уровня строки могут существенно замедлять массовые операции (например, INSERT ... SELECT на миллионы строк), так как выполняются для каждой строки. Триггеры уровня оператора менее затратны, но имеют ограниченный доступ к данным.
- Сложность отладки — триггеры выполняются неявно, что затрудняет трассировку ошибок. Ошибка в триггере может привести к откату всей транзакции.
- Скрытая логика — бизнес-правила, реализованные в триггерах, не видны в коде приложения, что может привести к неожиданному поведению и усложнить сопровождение.
- Ограничения рекурсии — триггеры могут вызывать друг друга (каскадные триггеры), что способно привести к бесконечной рекурсии. Большинство СУБД имеют механизмы защиты (максимальная глубина рекурсии, запрет на триггеры, модифицирующие ту же таблицу).
- Переносимость — синтаксис и возможности триггеров сильно различаются между СУБД (Oracle, PostgreSQL, MySQL, SQL Server, DB2), что затрудняет миграцию приложений.
Интересные факты
- В MySQL до версии 5.0.2 триггеры не поддерживались. В версии 5.0.2 появилась поддержка только для BEFORE-триггеров; AFTER-триггеры были добавлены позже.
- В PostgreSQL триггеры могут быть написаны на нескольких языках: PL/pgSQL, PL/Python, PL/Perl, PL/Tcl, а также на C.
- Стандарт SQL:2003 определяет триггеры как часть модуля «SQL/PSM» (Persistent Stored Modules), но реализация в разных СУБД часто выходит за рамки стандарта.
Источники
- ISO/IEC 9075-2:1999, SQL:1999 — Information technology — Database languages — SQL — Part 2: Foundation (SQL/Foundation)
- ISO/IEC 9075-2:2003, SQL:2003 — Information technology — Database languages — SQL — Part 2: Foundation (SQL/Foundation)
- Документация PostgreSQL 16: CREATE TRIGGER
- Документация Oracle Database 19c: CREATE TRIGGER
- Документация Microsoft SQL Server 2022: CREATE TRIGGER (Transact-SQL)
- Документация MySQL 8.0: CREATE TRIGGER Statement
- C. J. Date, «An Introduction to Database Systems», 8th Edition, Addison-Wesley, 2004
- Hector Garcia-Molina, Jeffrey D. Ullman, Jennifer Widom, «Database Systems: The Complete Book», 2nd Edition, Pearson, 2009
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →