Создание триггера (CREATE TRIGGER)
Функция
Определение оператора создания триггера.
Спецификация
::=[имя схемы.]имя триггера
{триггер на обработку данных | триггер на системные события}
::=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;