Шрифт:
* Триггер BEFORE UPDATE для инициатора запроса потребуется для обработки сложного правила, такого как описано в предыдущем пункте для проверки дат и, возможно, других критериев для осуществления правила и выбора корректного ключа.
Предположим, мы имеем следующие две таблицы:
CREATE TABLE LOOKUP (
UQ_ID SMALLINT NOT NULL UNIQUE,
VALUE1 VARCHAR(30) NOT NULL,
VALUE2 CHAR(2) NOT NULL,
START_DATE DATE,
END_DATE DATE) ;
COMMIT;
/* */
CREATE TABLE REQUESTOR (
ID INTEGER NOT NULL PRIMARY KEY,
LOOKUP_ID SMALLINT,
DATA VARCHAR(20)
TRANSAC_DATE TIMESTAMP NOT NULL) ;
COMMIT;
Теперь мы перейдем к установлению правил существования для двух таблиц. Мы планируем использовать исключения для остановки событий DML, которые нарушают целостность. Следовательно, вначале мы создадим эти исключения:
CREATE EXCEPTION NO_DELETE
'Can not delete row required by another table';
/* Нельзя удалять строку, нужную другой таблице */
CREATE EXCEPTION NOT_VALID_LOOKUP
'Not a valid lookup key';
/* Неверный ключ соответствия */
CREATE EXCEPTION NO_AUTHORITY
'You are not authorized to change this data';
/* Вы не можете изменять эти данные */
COMMIT;
Первый триггер выполняет проверку существования при попытке удалить строку соответствия:
SET TERM ^;
CREATE TRIGGER BD_LOOKUP FOR LOOKUP
ACTIVE BEFORE DELETE
AS
BEGIN
IF (EXISTS(
SELECT LOOKUP_ID FROM REQUESTOR
WHERE LOOKUP_ID = OLD.UQ_ID)) THEN
EXCEPTION NO_DELETE;
END ^
Другая сторона проверки существования: ключ соответствия не может быть назначен, если он отсутствует в таблице соответствия:
CREATE TRIGGER BA_REQUESTOR FOR REQUESTOR
ACTIVE BEFORE INSERT OR UPDATE
AS
BEGIN
IF (NEW.LOOKUP_ID IS NOT NULL
AND NOT EXISTS (
SELECT UQ_ID FROM LOOKUP
WHERE UQ_ID = NEW.LOOKUP_ID)) THEN
EXCEPTION NOT_VALID_LOOKUP;
END ^
Теперь мы можем добавить остальные триггеры для осуществления других нужных нам правил. Например, следующий триггер позволяет выполнять изменения или удаления в таблице соответствия только заданному пользователю:
CREATE TRIGGER BA_LOOKUP FOR LOOKUP
ACTIVE BEFORE UPDATE OR DELETE
AS
BEGIN
IF (CURRENT_USER <> 'CHIEFACCT') THEN
EXCEPTION NO_AUTHORITY;
END ^
Этот триггер будет проверять входной код соответствия, чтобы убедиться, что он правилен для периода транзакции, и будет корректировать его при необходимости:
CREATE TRIGGER BA_REQUESTORl FOR REQUESTOR
ACTIVE BEFORE INSERT OR UPDATE POSITION 1
AS
DECLARE VARIABLE LOOKUP_NUM SMALLINT;
DECLARE VARIABLE NEED_CHECK SMALLINT = 0;
BEGIN
IF (INSERTING AND NEW.LOOKUP_ID IS NOT NULL) THEN
NEED_CHECK = 1;
IF (UPDATING) THEN
IF (
(OLD.LOOKUP_ID IS NULL
AND NEW.LOOKUP_ID IS NOT NULL)
OR (OLD.LOOKUP_ID IS NOT NULL
AND NEW.LOOKUP_ID <> OLD.LOOKUP_ID)) THEN
NEED_CHECK = 1;
IF (NEED_CHECK = 1) THEN
BEGIN
SELECT L1.UQ_ID FROM LOOKUP L1
WHERE L1.START_DATE <= CAST(NEW.TRANSAC_DATE AS DATE)
AND L1.END_DATE >= CAST(NEW.TRANSAC_DATE AS DATE)
AND L1.VALUE2 = (SELECT L2.VALUE2 FROM LOOKUP L2
WHERE L2.UQ_ID = NEW.LOOKUP_ID)
INTO :LOOKUP_NUM;
NEW.LOOKUP_ID = LOOKUP_NUM;
END
END ^
COMMIT ^
SET TERM ;^
Изменение строк в той же таблице
Прежде чем решать вопрос об использовании триггера для изменения строк в той же самой таблице, внимательно рассмотрите возможность завершения цикла вложенной деятельности. Если триггер выполняет действие, которое приводит к вызову этого же триггера, или это действие вызывает другой триггер, который выполняет действие, вызывающее данный же триггер, то результатом будет бесконечный цикл. По этой причине важно убедиться, что действия триггера никогда не приведут к вызову триггером самого себя, даже опосредованно.