Следующий триггер проверяет подчиненную таблицу после добавления новой версии главной записи. Он может "видеть" подчиненные строки, ранее посланные в той же транзакции, которые имеют значение первичного ключа (NEW.ID) В столбце TEMP FK. В случае изменения, в отличие от добавления, он также может определить подчиненные строки, которыми уже "владеет" главная строка. Любые строки, которые соответствуют условию TEMP_FK, получают значение их внешнего ключа, а TEMP_FK устанавливается в NULL.
Если не найдено строк, соответствующих этому условию, то триггер добавляет "пустую" подчиненную строку.
SET TERM ^;
CREATE TRIGGER AI_MASTER FOR MASTER
ACTIVE AFTER INSERT OR UPDATE POSITION 1
AS
BEGIN
IF (NOT (EXISTS (
SELECT 1 FROM DETAIL WHERE MASTER_ID = NEW.ID
OR TEMP_FK = NEW.ID))) THEN
INSERT INTO DETAIL (MASTER_ID)
VALUES (NEW.ID);
ELSE
IF (NOT (EXISTS (
SELECT 1 FROM DETAIL WHERE MASTER_ID = NEW.ID))) THEN
UPDATE DETAIL SET
MASTER_ID = NEW.ID,
TEMP_FK = NULL
WHERE TEMP_FK = NEW.ID;
END ^
Подчиненная таблица получает автоматически сгенерированный ключ:
CREATE TRIGGER BI_DETAIL FOR DETAIL
ACTIVE BEFORE INSERT AS
BEGIN
IF (NEW.ID IS NULL) THEN
NEW.ID = GEN_ID(GEN_DETAIL, 1);
END ^
Следующий триггер BEFORE DELETE для подчиненной таблицы не позволит удалить строку, если она единственная:
CREATE TRIGGER BD_DETAIL FOR DETAIL
ACTIVE BEFORE DELETE POSITION 0
AS
BEGIN
IF (NOT (EXISTS (
SELECT 1 FROM DETAIL
WHERE MASTER_ID = OLD.MASTER_ID
AND ID <> OLD.ID)))
THEN
EXCEPTION CANNOT_DEL_DETAIL;
END ^
Сейчас у нас ситуация, когда обязательное отношение защищено настолько хорошо, что при попытке удалить главную строку этот триггер не позволит выполнить каскадное удаление. Нам нужно еще два триггера для главной таблицы, расширяющие действия триггеров, созданных системой для каскадного удаления. В триггере BEFORE DELETE для главной таблицы мы заполняем пустым значением внешний ключ подчиненной таблицы и устанавливаем значение столбца TEMP FK. После того как будет
выполнено удаление строки главной таблицы, мы возвращаемся назад и удаляем подчиненную строку.
CREATE TRIGGER BD_MASTER FOR MASTER
ACTIVE BEFORE DELETE
AS
BEGIN
UPDATE DETAIL
SET MASTER_ID = NULL,
TEMP_FK = OLD.ID
WHERE MASTER_ID = OLD.ID;
END ^
/* */
CREATE TRIGGER AD_MASTER FOR MASTER
ACTIVE AFTER DELETE AS
BEGIN
DELETE FROM DETAIL
WHERE TEMP_FK = OLD.ID;
END ^
COMMIT ^
SET TERM; ^
К сожалению, этот пример вряд ли удовлетворит всем требованиям обязательных отношений. Обычно необходимо рассмотреть некоторые другие факторы в терминах требований бизнес-правил и интерфейса программирования. Для логики триггеров редко не появляются такие причины.
Поддержка ссылочной целостности
Формально - или декларативно - ограничения ссылочной целостности должны использоваться везде, где они практически нужны. Проверка, выполняющаяся при поддержке формальных ограничениях ссылочной целостности, осуществляется внутренне триггерами. Если вы хотите расширить действия по ссылочной целостности, вам нужно создать для этого триггеры.
Некоторые твердолобые люди годами разрабатывают программы под Firebird и InterBase, избегая декларативной ссылочной целостности, и используют для этого триггеры. Не существует технических причин в любой версии Firebird исключать декларативную ссылочную целостность, если она вам нужна- это работает очень хорошо и не требует многих ресурсов.
Однако декларативная ссылочная целостность требует внешнего ключа, который в свою очередь требует обязательного индекса. В Firebird пока не существует способа поддерживать внешние ключи без обязательного индекса. Существует ситуация, достаточно общая, чтобы обратить на нее внимание, когда индекс для внешнего ключа оказывается весьма плохим с точки зрения производительности в запросах, включающих данную таблицу. Здесь следует исключить формальные ссылочные отношения. Такая ситуация возникает, когда при проектировании появляются таблицы, часто называемые таблицами "соответствия", "системными" или "управляющими".
Таблица соответствия (lookup) или управляющая таблица обычно является статичной таблицей с небольшим количеством строк, которая может быть использована похожим образом в нескольких различных контекстах. Она содержит небольшой первичный ключ и поле описания, коэффициент для вычисления или некоторое правило, к которым нужно обратиться процессу. Примерами являются таблицы налогов, типы счетов, типы транзакций, коды причин и т.д. Процесс нормализации будет нарушен, если системная таблица, связанная с другими таблицами (обычно со многими), будет сохранять ключи соответствия в пользовательской таблице. Поскольку одна строка в таблице соответствия предоставляет информацию для многих строк, строгое соблюдение правил реляционного анализа часто приводит к внешним ключам, помещенным в столбцы ключей соответствия в пользовательских таблицах.