7. Триггеры
Триггер — это PL/SQL блок, который хранится в базе данных и срабатывает (выполняется) в ответ на указанное событие.
Триггер может быть определён на таблицу, представление, схему (владельца схемы) или базу данных (всех пользователей).
Типы событий триггеров:
- DML-команда (
DELETE,INSERTилиUPDATE). - DDL-команды (
CREATE,ALTERилиDROP). - Операции базы данных, такие как
SERVERERROR,LOGON,LOGOFF,STARTUPилиSHUTDOWN.
Пример создания триггера:
CREATE OR REPLACE TRIGGER check_salary_trg
-- перед INSERT в Employees или UPDATE столбцов Salary или Job_ID
BEFORE INSERT OR UPDATE OF Salary, Job_ID ON Employees
[REFERENCING OLD AS old_name | NEW AS new_name] -- можно переименовывать OLD и NEW
FOR EACH ROW -- строковый триггер, для каждой строки (не для всей DML-команды)
BEGIN
-- NEW — объект типа строки Employees, хранит строку после DML-команды
-- OLD — то же самое, но до DML-операции
check_salary(:NEW.Job_ID, :NEW.Salary); -- вызов процедуры check_salary
END;
/
Триггеры делятся на:
- Триггеры базы данных. Срабатывают при возникновении DML, DDL или системного события в схеме или базе данных.
- Триггеры приложений. Срабатывают при возникновении события в конкретном приложении.
Триггеры можно использовать для:
- Безопасности.
- Аудита.
- Целостности данных.
- Ссылочной целостности.
- Репликации (копирования) таблиц.
- Автоматического вычисления производных данных.
- Ведения журнала событий.
Допустимые типы триггеров:
- Простые DML-триггеры (
BEFORE,AFTER,INSTEAD OF(только для view)). - Составные (compound) триггеры.
- Не DML-триггеры (DDL-события, события базы данных).
Триггеры бывают:
- Уровня команды (по умолчанию). Срабатывают один раз на событие триггера, даже тогда, когда не затронута ни одна из строк.
- Строковыми. Нужно прописать
FOR EACH ROW, срабатывают один раз на каждую затронутую строку по событию триггера, не срабатывают, если событие не затронуло ни одной строки.
Можно так же задавать условия для выполнения триггеров.
В теле триггера можно использовать конструкции (если триггер состоит из нескольких DML-операций):
IF DELETING,IF UPDATING,IF INSERTING,
чтобы определить, какое событие вызвало триггер, который слушает несколько событий.
CREATE OR REPLACE TRIGGER triggerName
[BEFORE | AFTER] INSERT OR UPDATE OR DELETE ON Table_Name
BEGIN
IF DELETING THEN
-- ...
ELSIF UPDATING THEN
-- ...
ELSIF INSERTING THEN
-- ...
END IF;
END;
/
Можно так же использовать WHEN для строковых триггеров.
CREATE OR REPLACE TRIGGER check_salary_trg
BEFORE INSERT OR UPDATE OF Salary, Job_ID ON Employees
FOR EACH ROW
/*
проверяем, что мы действительно обновляем столбец Job_ID или Salary
или же вставляем новую строку
(без этого триггер сработает на UPDATE, даже если фактическое значение не изменилось).
*/
WHEN (
OLD.Job_ID IS NULL OR OLD.Salary IS NULL
OR OLD.Job_ID != NEW.Job_ID OR OLD.Salary != NEW.Salary
)
-- Заметим, что внутри WHEN пишем «OLD», а внутри BEGIN..END — «:OLD».
BEGIN
check_salary(:NEW.Job_ID, :NEW.Salary);
END;
/
О NEW и OLD (доступны только в строковых триггерах):
| DML-команда | OLD | NEW |
|---|---|---|
INSERT | NULL | введённое значение |
UPDATE | значение до обновления | значение после обновления |
DELETE | удаляемое значение | NULL |
Краткое описание модели выполнения триггера:
- Выполняются все
BEFOREтриггеры уровня команды. - Для каждой строки, затронутой триггером:
- Выполняются все строковые
BEFOREтриггеры - Выполняется DML-команда и проверка ограничений целостности.
- Выполняются все строковые
AFTERтриггеры.
- Выполняются все строковые
- Выполняются все
AFTERтриггеры уровня команды.
У триггера есть 2 состояния: ENABLE и DISABLE. Синтаксис:
CREATE OR REPLACE TRIGGER triggerName
BEFORE INSERT ON tableName FOR EACH ROW
DISABLE -- создание выключенного триггера
BEGIN
-- код
END;
/
-- триггер можно выключить/включить, например, так:
ALTER TRIGGER triggerName DISABLE; -- выключаем
ALTER TRIGGER triggerName ENABLE; -- включаем
- Протестируйте каждую операцию с данными, вызывающую срабатывание триггера, а также операции с данными, не вызывающие триггеры.
- В каждом случае протестируйте условие
WHEN. - Вызовите срабатывание триггера непосредственно из операции с основными данными, а также косвенно из процедуры.
- Проверьте влияния триггера на другие триггеры.
- Проверьте влияния других триггеров на данный триггер.
Информацию о триггерах можно посмотреть в словарях USER_OBJECTS, USER/ALL/DBA_TRIGGERS. Синтаксис ошибок триггеров можно посмотреть в словаре USER_ERRORS.
Составной (compound) триггер — это один триггер на таблицу, позволяющий задать действия для каждой из следующих четырёх точек синхронизации:
- Перед вызывающей командой.
- Перед каждой строкой, на которую влияет вызывающая команда.
- После каждой строки, на которую влияет вызывающая команда.
- После вызывающей команды.
Мутирующая таблица — это:
- Таблица, которая изменяется с помощью команды
UPDATE,DELETEилиINSERT, или - Таблица, которая может быть обновлена под действием ограничения
DELETE CASCADE.
Составные триггеры можно использовать для:
- Программирования подхода, в котором вы хотите, чтобы действия, которые вы выполняете для разных точек синхронизации, могли совместно использовать общие данные.
- Накопления строк, предназначенных для второй таблицы; так что вы можете периодически их вставлять.
- Того, чтобы избежать ошибки мутирующей таблицы
(ORA-04091), разрешив накопление строк, предназначенных для второй таблицы, а затем их массовую вставку.
Ограничения составных триггеров:
- Составной триггер должен быть DML-триггером и определяться на таблицу или представление.
- Тело составного триггера должно быть блоком составного триггера, написанным на языке PL/SQL.
- Тело составного триггера не может иметь блок Инициализации, поэтому оно не может содержать раздел Исключений.
- Исключение, которое происходит в одном разделе, должно быть обработано в том же разделе. Он не может передать управление другому разделу.
:OLDи:NEWнельзя использовать в разделах Объявления,BEFORE STATEMENTиAFTER STATEMENT.- Только раздел
BEFORE EACH ROWможет изменять значение:NEW. - Порядок срабатывания составных триггеров не гарантируется, если вы не используете
FOLLOWS.
Ограничение триггеров на мутирующие таблицы:
- Сессия, использующая вызывающий триггер оператор, не может изменять мутирующую таблицу или выполнять к ней запросы.
- Это ограничение не позволяет триггеру видеть несогласованный набор данных.
- Это ограничение применяется ко всем триггерам, использующим раздел
FOR EACH ROW. - Представления, изменённые в триггерах
INSTEAD OF, не считаются мутирующими.
Триггеры LOGON и LOGOFF. Пример:
CREATE OR REPLACE TRIGGER logon_trig
AFTER LOGON ON SCHEMA
BEGIN
INSERT INTO log_trig_table(user_id, log_date, action)
VALUES (USER, SYSDATE, 'Logging on');
END;
/
CREATE OR REPLACE TRIGGER logoff_trig
BEFORE LOGOFF ON SCHEMA
BEGIN
INSERT INTO log_trig_table(user_id, log_date, action)
VALUES (USER, SYSDATE, 'Logging off');
END;
/
Можно использовать команду CALL внутри триггера:
CREATE OR REPLACE PROCEDURE log_execution IS
BEGIN
DBMS_OUTPUT.PUT_LINE('log_exection: Employee Inserted');
END;
/
CREATE OR REPLACE TRIGGER log_employee
BEFORE INSERT ON EMPLOYEES
CALL log_execution -- точка с запятой не требуется
/
Преимущества триггеров на события базы данных:
- Повышают безопасность данных:
- Обеспечивают расширенные и сложные проверки безопасности.
- Обеспечивают расширенный и комплексный аудит.
- Повышают целостность данных:
- Применение ограничений динамической целостности данных.
- Применение сложных ограничений ссылочной целостности.
- Гарантия того, что связанные операции выполняются вместе неявно.
Системные привилегии, необходимые для управления триггерами:
- Привилегия
CREATE/ALTER/DROP (ANY) TRIGGER, которая позволяет создавать триггеры в любой схеме. - Привилегия
ADMINISTER DATABASE TRIGGER, которая позволяет создавать триггеры базы данных. - Привилегия
EXECUTE(если триггер ссылается на объекты, которых нет в схеме).
- Триггеры разрабатываются для:
- Выполнения связанных действий.
- Централизации глобальных операций.
- Не нужно разрабатывать триггеры:
- Если функциональность уже встроена в сервер Oracle.
- Которые дублируют уже существующие триггеры.
- Можно создавать хранимые процедуры и вызывать их в триггере, если PL/SQL код очень длинный.
- Чрезмерное использование триггеров может привести к сложным взаимозависимостям, которые трудно поддерживать в больших приложениях.