Firebird РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ
Шрифт:
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 ;^
Изменение строк в той же таблице
Прежде чем решать вопрос об использовании триггера для изменения строк в той же самой таблице, внимательно рассмотрите возможность завершения цикла вложенной деятельности. Если триггер выполняет действие, которое приводит к вызову этого же триггера, или это действие вызывает другой триггер, который выполняет действие, вызывающее данный же триггер, то результатом будет бесконечный цикл. По этой причине важно убедиться, что действия триггера никогда не приведут к вызову триггером самого себя, даже опосредованно.
Если при проектировании базы данных вы подошли к моменту, когда вам нужно написать
Ссылающиеся на себя таблицы, которые реализуют древовидные структуры [125] , являются особым случаем [126] . Каждая строка в подобной таблице является узлом дерева и может иметь зависимые строки. Любой узел потенциально может иметь две роли: одна- роль родителя для узлов ниже него, а другая - роль потомка узла более высокого уровня. Триггеры, скорее всего, будут нужны для всех событий DML: для модификации поведения ограничений ссылочной целостности и для поддержания мета- таблиц (графов), используемых в некоторых иерархических алгоритмах, делающих доступной запросам геометрию дерева. Триггеры для деревьев всегда должны быть спроектированы с условиями и переходами, которые защищают структуру от бесконечных циклов.
125
Проектирование древовидных структур в реляционных базах данных само по себе является наукой. Будучи очаровательным, это все же выходит за пределы данного руководства. Найдите в Интернете написанную Joe Celko книгу на эту тему: Joe Celko's Trees and Hierarchies in SQL for Smarties (Morgan Kaufmann, 2004).
126
Ряд очень полезных статей по реализации древовидных структур в РСУБД вы сможете найти в соответствующем разделе страницы www.ibase.ru/develop.htm.
– Прим. науч. ред.
Никогда не пытайтесь использовать оператор SQL для изменения или удаления той же самой строки, с которой оперирует триггер. Например, не рекомендуется использовать следующий вариант:
CREATE TRIGGER 0_30_SILLY FOR ATABLE
BEFORE UPDATE
AS
BEGIN
UPDATE ATABLE SET ACOLUMN - NEW.ACOLUMN
WHERE ID = NEW.ID;
END ^
Всегда используйте переменные NEW для модификаций в той же строке и никогда не пытайтесь удалять ту же строку в триггере.
Изменение триггеров
Firebird 1.0.x предоставляет только один способ изменения триггеров при использовании операторов DDL, a Firebird 1.5 добавляет еще один.
* ALTER TRIGGER изменяет определение существующего модуля триггера, сохраняя его зависимости от других объектов. Он может быть использован с минимальным беспокойством по поводу деактивации триггера.
* CREATE OR ALTER TRIGGER (версия 1.5 и выше) создает модуль триггера, если он не существует, и работает точно так же, как и CREATE TRIGGER. В противном случае применяются правила ALTER, и зависимости сохраняются.