Шрифт:
Если при проектировании базы данных вы подошли к моменту, когда вам нужно написать триггер, который реализует зависимости данных между строками одной и той же таблицы, то, скорее всего, это сигнал о неправильной нормализации таблиц, если только эта зависимость не относится к иерархической структуре. Если фрагмент структуры строки влияет на (или на него влияет) изменение состояния другой строки, то такой сегмент должен быть в результате нормализации перенесен в другую таблицу с внешним ключом для поддержания правила зависимости.
Ссылающиеся на себя таблицы, которые реализуют древовидные структуры [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, и зависимости сохраняются.
Любая операция завершится с исключением при любой попытке изменений, которая отменяет зависимости.
Синтаксис для изменения триггеров
Синтаксис:
{ALTER TRIGGER ИМЯ} |
{CREATE OR ALTER TRIGGER имя FOR {таблица | просмотр}
[ACTIVE | INACTIVE]
[{BEFORE | AFTER} {DELETE | INSERT | UPDATE}]
[POSITION число]
AS <тело-триггера>;
Предложение FOR ИМЯ, применяемое в CREATE TRIGGER, опускается, ALTER TRIGGER не может использоваться для изменения таблицы, с которой ассоциирован триггер.
Когда вы используете ALTER TRIGGER для изменения только заголовка, оператор требует по меньшей мере одного изменяемого атрибута после имени триггера. Любой атрибут заголовка, опущенный в этом операторе, остается неизменным.
Следующий оператор деактивирует триггер SAVE_SALARY_CHANGE:
ALTER TRIGGER SAVE SALARY CHANGE INACTIVE;
Если изменяется индикатор фазы (BEFORE или AFTER), ТО событие (UPDATE, INSERT или DELETE) также должно быть указано. Например, следующий оператор заново активирует триггер SAVE_SALARY_CHANGE и указывает, что он будет выполняться до изменения, а не после:
ALTER TRIGGER SAVE_SALARY_CHANGE
ACTIVE BEFORE UPDATE;
Любое изменение тела триггера приводит к тому, что новое определение тела заменяет старое определение. Оператор ALTER TRIGGER не должен содержать никакую информацию заголовка, кроме имени триггера.
Например, следующий оператор изменяет триггер SET CUST NO, который был создан с таким определением:
CREATE TRIGGER SET_CUST_NO FOR CUSTOMER
BEFORE INSERT
AS
BEGIN
IF (NEW.CUST_NO IS NULL) THEN
NEW.CUST_NO = GEN_ID(CUST_NO_GEN, 1);
END^
Мы изменим этот триггер, чтобы он добавлял новую строку в таблицу NEW CUSTOMERS каждый раз, когда новая строка добавляется в таблицу CUSTOMER:
SET TERM ^;