Firebird РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ
Шрифт:
Переменные события
В Firebird появляются логические контекстные переменные INSERTING, UPDATING и DELETING, чтобы поддерживать условные переходы для триггеров, используемых для нескольких событий. Возможным синтаксисом ветвления может быть:
IF ({INSERTING | UPDATING | DELETING}
OR {UPDATING | DELETING | INSERTING}
[OR {DELETING | INSERTING | UPDATING}]) THEN ...
Работа этих полезных предикатов иллюстрируется в дальнейших примерах этой главы.
Переменные NEW и OLD
Контекстные переменные NEW и OLD являются расширениями PSQL, специфичными
120
NEW.* и OLD.* также допустимы в DDL для ограничений CHECK на уровне таблицы - например, CREATE TABLE BLAH (ID INTEGER, DATA INTEGER, CONSTRAINT CHECK_INCR CHECK(NEW.DATA > OLD.DATA)).
Значения OLD.* (если доступны) могут использоваться в триггерах как переменные, но изменение значения не влияет на сохраненное старое значение [121] . Значения NEW.* (если доступны) могут использоваться для чтения/записи в фазе BEFORE и только для чтения в фазе AFTER. Если вы хотите манипулировать ими как переменными значениями в триггере AFTER, присвойте их значения локальным переменным и обращайтесь к этим локальным переменным.
121
В Firebird 2.0 модификация значений OLD. * явно запрещена и будет вызывать ошибку при попытке присвоения значений переменным OLL. * как при выполнении, так и при создании триггера, Также запрещена модификация значений NEW, * В триггерах AFТER. Прuм. науч. ред.
Для использования мощи триггеров Firebird в разработке баз данных при отслеживании целостности данных, независимо от людей и внешних программ, переменные NEW и OLD являются основным инструментом. Они могут быть использованы для:
* получения допустимых значений по умолчанию в некоторых условиях;
* проверки и при необходимости преобразования входных данных пользователя;
* получения ключей и значений для выполнения автоматических обновлений в других таблицах;
* реализации автоинкрементных ключей средствами генераторов.
Новые значения для строки могут быть изменены только в действиях BEFORE. Если триггер, запускаемый в фазе AFTER, попытается присвоить значение столбцу NEW, это не даст никакого результата.
Все значения NEW можно перезаписывать в фазе BEFORE, они немедленно принимают новые назначенные им значения. Новая версия записи получит переназначенные значения, только когда все триггеры BEFORE будут завершены. С этого момента значения NEW становятся значениями только для чтения. Следовательно, если у вас несколько триггеров изменяют одни и те же значения NEW, важно, чтобы все они имели различные номера POSITION,
Рекомендованное использование в Firebird триггеров BEFORE INSERT - реализация в стиле @IDENTITY автоинкрементных первичных ключей. Эта техника проста, и большинство разработчиков Firebird могут написать такие триггеры во сне. Она включает два шага:
1. Создание генератора для генерации уникальных чисел ключа.
2. Написание триггера BEFORE INSERT для таблицы.
Для иллюстрации этой техники мы реализуем автоинкрементный первичный ключ для таблицы CUSTOMER, у которой первичный ключ CUSTOMER_ID- столбец целого типа BIGINT (версия 1.5) или NUMERIC(18,0) (версия 1.0.x). В диалекте 1 базы данных CUSTOMER_ID должен иметь тип INTEGER.
Во-первых, создадим генератор:
CREATE GENERATOR GEN_PK_CUSTOMER;
Затем создадим триггер:
CREATE TRIGGER BI_CUSTOMER FOR CUSTOMER
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
IF (NEW.CUSTOMER_ID IS NULL) THEN
NEW. CUSTOMER_ID = GEN_ID (GEN_PK_CUSTOMER, 1) ;
END ^
COMMIT ^
Когда выполняется добавление, CUSTOMER ID сознательно не указывается во входном списке оператора INSERT:
INSERT INTO CUSTOMER (
LAST_NAME,
FIRST_NAME,
...)
VALUES (?, ?, ...);
Без триггера этот оператор вызовет исключение, потому что первичный ключ не может иметь пустого значения. Однако триггер BEFORE INSERT выполняется до проверки этого ограничения, он контролирует, что CUSTOMER ID имеет пустое значение, и выполняет свое действие.
Если триггер может делать это для меня, то вы можете спросить, зачем нужно выполнять проверку на NULL?
Для приложения может быть полезным знать, какое будет значение первичного ключа новой строки без необходимости ожидать завершения транзакции. Например, это общее требование при создании "главной" записи и связи с ней "подчиненных" записей обычно с помощью внешнего ключа в одной транзакции. Довольно неуклюже - а иногда и рискованно - нарушать атомарность задачи создания главная- подчиненная, подтверждая создание главной для получения необходимого вам значения внешнего ключа для подчиненных записей, полагаясь только на триггер.
Приложения, написанные для Firebird, имеют преимущества, благодаря одной особой характеристике генераторов: они не зависят от пользовательских транзакций. Однажды сгенерированное значение не может быть выдано ни одной другой транзакции и не может быть отменено.
Быстрый запрос в его собственной транзакции [122] возвращает значение
SELECT GEN_ID (GEN_PK_CUSTOMER, 1) AS RESULT
FROM RDB$DATABASE;
Если в вашем триггере опущена проверка на пустое значение и просто выполняется:
122
Некоторые компоненты интерфейса предоставляют методы или свойства для автоматического выполнения этой маленькой задачи.