Создание триггера
Функция
Определение оператора создания триггера.
Спецификация
::=
[имя схемы.]имя триггера
{триггер на обработку данных | триггер на системные события}
::=
ON [имя схемы.]имя таблицы
[FOR EACH {ROW|STATEMENT}]
[OLD [AS] псевдоним для старой записи]
[NEW [AS] псевдоним для новой записи]
EXECUTE [FOR DEBUG] тело триггера;
::=
::=
ON {имя пользователя|DATABASE}
::=
::=
::=
Синтаксические правила
-
< Имя таблицы >
должно задавать объект (базовую таблицу или представление), для которого создается триггер. -
< Имя схемы >
задает имя схемы, владельцем которой является текущий пользователь. Создавать триггеры в «чужих» схемах запрещено. -
Опция
OR REPLACE
заставляет удалять существующий в БД триггер и создавать его под тем же именем, но с указанными параметрами. -
Опция
IF NOT EXISTS
отменяет выполнение оператора, если указанный триггер уже существует в БД. -
Одновременное использование опций
IF NOT EXISTS
иOR REPLACE
запрещено. -
< Операция >
задает операцию по обработке данных таблицы, при выполнении которой должен срабатывать триггер:-
INSERT
– при добавлении данных; -
DELETE
– при удалении данных; -
UPDATE
– при корректировке данных.
-
-
Если указана операция
UPDATE
без конструкцииOF
, триггер будет срабатывать при корректировке любого поля записи таблицы. -
< Имя столбца >
должно ссылаться на столбец в< имени таблицы >
. -
Если указана операция
UPDATE OF < столбец >[, …]
, триггер будет срабатывать при корректировке только перечисленных столбцов записи таблицы. -
Опции
BEFORE
,AFTER
,INSTEAD OF
определяют момент срабатывания триггера при выполнении операций обработки данных:-
BEFORE
– перед выполнением< операции >
; -
AFTER
– после выполнения< операции >
; -
INSTEAD OF
– взамен выполнения< операции >
.
-
-
Конструкция
INSTEAD OF
применима только для базовых таблиц (не представлений). -
Конструкция
INSTEAD OF
не применима для таблиц, работающих в циклическом режиме, поскольку такие триггеры препятствуют удалению записей (см. операторALTER TABLE … SET RECORDS LIMIT
пункта «Модификация таблицы»). -
Конструкция
INSTEAD OF
допустима как для режимаFOR EACH ROW
, так и дляFOR STATEMENT
. Она позволяет выполнить проверку достоверности значений, которые должны быть добавлены, обновлены или удалены, и, в случае их достоверности, выполняет соответствующую операцию. Например, при обработке заказа на некоторый товар триггерINSTEAD OF UPDATE
может проверить, есть ли в таблице заказов строка с требуемым товаром. Если да, сумма заказа будет увеличена (операцияUPDATE
), если нет – запись будет добавлена (операцияINSERT
). -
Если на операцию создано несколько триггеров одного времени действия, они будут выполняться в порядке их системных номеров, который обычно, но не всегда, совпадает с порядком создания триггеров.
-
Конструкция
FOR EACH…
определяет сферу действия триггера:-
ROW
– для каждой записи. Триггер будет срабатывать во время выполнения< операции >
при обработке каждой записи таблицы. Возврат триггером значенияTRUE
разрешает, аFALSE
– запрещает выполнение операции только для текущей записи (имеет смысл применительно кBEFORE
); -
STATEMENT
– для каждого оператора. Триггер будет срабатывать один раз при выполнении оператора, соответствующего заданной< операции >
. Возврат триггеромBEFORE ... FOR EACH STATEMENT
значенияFALSE
отменяет действие всей операции.
-
-
Если конструкция
FOR EACH…
не задана, по умолчанию используетсяFOR EACH STATEMENT
. -
Если один из BEFORE-триггеров запретил операцию, то последующие AFTER-триггеры выполняться не будут.
Примечание
Например, в операторе
UPDATE AUTO SET MAKE ='BMW' where PERSONID IN (SELECT PERSONID FROM PERSON WHERE SALARY BETWEEN 2000 AND 50000)
; триггер наUPDATE
при указанииFOR EACH ROW
выполнится столько раз, сколько записей выбирается по условиюWHERE
; при указанииFOR EACH STATEMENT
– только один раз (в случае выполнения условияWHERE
). -
< Псевдоним для старой записи >
задает префикс, используемый для обращения к старым значениям столбца. Если не задан, по умолчанию используетсяOLD
. -
< Псевдоним для новой записи >
задает префикс, используемый для обращения к новым значениям столбца. Если не задан, по умолчанию используетсяNEW
.update auto set old.color=new.color where personid=256; update auto set "Старое".color="Новое".color where personid=256;
-
Понятия «старое» и «новое» значение столбца применимы только для операции
UPDATE
, независимо от времени действия триггера. -
Для операции
INSERT
имеет смысл только «новое» значение столбца. -
Для операции
DELETE
имеет смысл только «старое» значение столбца. -
Запрещено создавать триггер на таблицу другого пользователя.
-
Имя триггера должно быть уникально в пределах
< имя схемы >
. -
Опции
< триггер на системные события >
:-
AFTER LOGON
– активизирует триггер после успешного открытия канала с СУБД (после установления соединения с СУБД). Если триггер возвращает ошибку, то для всех пользователей, за исключениемLINTER_SYSTEM_USER
, выполняется очистка канала; -
BEFORE LOGOFF
– активизирует триггер перед закрытием канала (перед закрытием соединения по инициативе пользователя или при выполнении командKILL
,CKIL
интерфейса нижнего уровня). Для курсорных запросов эти триггеры выполнять не следует; -
DATABASE
– активизирует триггер при открытии/закрытии любого канала; -
< имя пользователя >
– активизирует триггер при открытии/закрытии канала для указанного пользователя.
-
-
Триггеры
AFTER LOGON
/BEFORE LOGOFF
может создавать/модифицировать/удалять только создатель БД. -
Тело триггера (конструкция
EXECUTE < тело триггера >
) должно быть написано на процедурном языке СУБД ЛИНТЕР (см. документ «Процедурный язык», раздел «Элементы языка»). -
Опция
FOR DEBUG
позволяет выполнять отладку триггера с помощью отладчика триггеров и хранимых процедур СУБД ЛИНТЕР.
Общие правила
-
На каждую операцию можно создавать не более 255 триггеров.
-
Для хранения тела оттранслированной процедуры, возвращающей тип данных «курсор», в БД должна существовать системная таблица
$$$PRCD
. -
Порядок вызова триггеров с разными значениями
< время действия >
следующий:-
BEFORE EACH STATEMENT
; -
BEFORE EACH ROW
; -
AFTER EACH ROW
; -
AFTER EACH STATEMENT
.
-
-
Триггер
BEFORE EACH ROW
может изменить значение поля записи, помещаемое в БД, для операторовINSERT
иUPDATE
. -
Возврат триггером
BEFORE ... FOR EACH ROW
значенияFALSE
означает отмену действия операции только для текущей записи. -
В режиме
FOR STATEMENT
перед обработкой триггеров на условиеBEFORE STATEMENT
происходит выполнение всех триггеров на условиеINSTEAD OF
. Если был выполнен хотя бы один триггерINSTEAD OF
(даже если он вернул ошибку), дальнейшая обработка запроса и всех его триггеров прекращается. -
В режиме
FOR EACH ROW
перед обработкой триггеров на условиеBEFORE
происходит выполнение всех триггеров на условиеINSTEAD OF
. Если был выполнен хотя бы один триггерINSTEAD OF
(даже если он вернул ошибку), обработка записи и триггеровFOR EACH ROW
прекращается, происходит переход к обработке следующей записи. При этом триггеры на условиеFOR STATEMENT
(BEFORE
иAFTER
) выполняются в обычном режиме. -
В момент активизации
< триггера на системные события >
канал получает статус «занят», что дает возможность триггеру выполняться без помех. -
В триггере на системные события все операторы выполняются от имени пользователя, который устанавливает/разрывает соединение, в триггере на обработку данных все операторы выполняются от имени пользователя, который выполняет операцию.
-
При наличии нескольких
< триггеров на системные события >
сначала выполняются триггеры для всей БД, затем – для указанного пользователя.create or replace table test (i int, ch char( 40 )); create or replace trigger test1 after logon on SYSTEM execute code execute direct "insert into test values (1,'After logon for user 1');"; return TRUE; end; create or replace trigger test2 after logon on DATABASE execute code execute direct "insert into test values (1,'After logon for database 1');"; return TRUE; end; create or replace trigger test3 after logon on SYSTEM execute code execute direct "insert into test values (1,'After logon for user 2');"; return TRUE; end; create or replace trigger test4 after logon on DATABASE execute code execute direct "insert into test values (1,'After logon for database 2');"; return TRUE; end; username SYSTEM/MANAGER8 select * from test; I CH - -- | 1|After logon for database 1 | | 1|After logon for database 2 | | 1|After logon for user 1 | | 1|After logon for user 2 |
-
Если один из триггеров вернул ошибку, остальные триггеры не выполняются.
create or replace table test (i int, ch char( 40 )); create or replace trigger test1 after logon on SYSTEM execute code execute direct "insert into test values (1,'After logon for user 1');"; return FALSE; end; create or replace trigger test2 after logon on DATABASE execute code execute direct "insert into test values (1,'After logon for database 1');"; return FALSE; end; create or replace trigger test3 after logon on SYSTEM execute code execute direct "insert into test values (1,'After logon for user 2');"; return FALSE; end; create or replace trigger test4 after logon on DATABASE execute code execute direct "insert into test values (1,'After logon for database 2');"; return FALSE; end; username SYSTEM/MANAGER8 // триггер test1 вернул FALSE, поэтому триггеры test2, test3, test4 // выполняться не будут, канал для пользователя SYSTEM открыт (т.к. это // создатель БД – создателю БД нельзя случайно запретить подсоединение к БД // созданием LOGON-триггера, возвращающего значение FALSE) create or replace user TTT identified by '12345678'; username TTT/12345678 // ошибка – нет привилегий 1022 (возвращается в случае возврата // LOGON-триггером значения FALSE для любого пользователя, кроме создателя БД) // триггер test1 вернул FALSE, поэтому триггеры test1, test2, test3, test4 выполняться не будут username SYSTEM/MANAGER8 select * from test; I CH - -- | 1|After logon for database 1 | | 1|After logon for database 1 |
-
По окончании работы триггеров статус канала восстанавливается.
-
В случае занятости транслятора процедурного языка ядро СУБД ЛИНТЕР будет ожидать его освобождения.
Пример
CREATE TRIGGER UPDPERSON AFTER UPDATE ON PERSON FOR EACH ROW EXECUTE CODE if old.personid = new.personid then execute direct "insert into journal values('PERSON','UPDATE'," + itoa(old.personid) + ",sysdate, 'personid not changed');"; // return true; // endif execute direct "insert into journal values('PERSON','UPDATE'," + itoa(old.personid) + ",sysdate, 'set personid ='+itoa(new.personid)+ ' – cause update for auto');"; // execute direct "update auto set personid=" + itoa(new.personid) + " where personid = " + itoa(old.personid) + ";"; // END;