Firebird РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ
Шрифт:
. . .
AS
NEW.CUSTOMER_ID = GEN_ID (GEN_PK_CUSTOMER, 1);
END ^
то значение, полученное приложением, будет перекрыто вторым "дерганьем" генератора, что нарушит связь с подчиненными записями.
Эта ситуация не является аргументом в пользу генерации ключей только в триггерах. Наоборот, триггер с проверкой на NULL обеспечивает реализацию бизнес-правил при любых условиях.
! ! !
ВНИМАНИЕ! В разработках, где нет хорошей интеграции работы приложений различных разработчиков, или
. ! .
Переменная NEW может быть использована для преобразования значения в нечто другое. Общий трюк заключается в использовании триггера (или пары триггеров в версии 1.0.А-) для поддержания "заменителя" столбца для выполнения нечувствительных к регистру поисков по другому столбцу, который может содержать смешанные значения регистра. Триггер читает значение NEW столбца со смешанным регистром, конвертирует его в верхний регистр и записывает в значение NEW столбца поиска. Такой столбец-"заменитель" должен иметь ограничение NOT NULL для гарантии того, что в нем всегда будет значение для поиска:
CREATE TABLE MEMBER (
MEMBER_ID INTEGER NOT NULL PRIMARY KEY,
LAST_NAME VARCHAR (40) NOT NULL,
FIRST_NAME VARCHAR (35),
PROXY_LAST_NAME VARCHAR (40),
MEMBER_TYPE CHAR(3) NOT NULL,
MEMBERSHIP_NUM VARCHAR(13) ,
. . . . );
COMMIT;
/* */
SET TERM ^;
CREATE TRIGGER BA_MEMBER1 FOR MEMBER
ACTIVE BEFORE INSERT OR UPDATE
POSITION 0
AS
BEGIN
. . .
NEW. PROXY_LAST_NAME = UPPER(NEW.LAST_HAME) ;
. . .
END ^
Возможны любые виды преобразований. Предположим, мы хотим получить количество элементов (MEMBERSHIP_NUM), собранное из MEMBER_TYPE, за которым следует строка из десяти цифр, заполненная слева нулями и полученная из сгенерированного первичного ключа таблицы MEMBER. С помощью автоматической генерации в триггере BEFORE INSERT мы можем это осуществить [123] :
CREATE TRIGGER BI_MEMBER2 FOR MEMBER
ACTIVE BEFORE
123
Такая манипуляция будет гораздо более простой, если использовать внешнюю функцию для вычисления длины преобразованного целого числа. Я выбираю - может быть немного с другой точки зрения - демонстрацию того, что можно выполнить манипуляцию строками без использования внешних функций.
INSERT POSITION 2
AS
DECLARE VARIABLE ID AS STRING VARCHAR (10);
BEGIN
ID_AS_STRING = CAST (NEW. ID AS VARCHAR (10)) ;
WHILE (NOT (ID_AS_STRING LIKE ' %'))
/* 10-символьная маска */
DO
ID_AS_STRING = '0' || ID_AS_STRING;
NEW.MEMBERSHIP_NUM = NEW.MEMBER_TYPE || ID_AS_STRING;
END ^
Триггеры могут улучшить стандартные ограничения SQL, когда они используются для проверки входных данных и применения
SQL предоставляет ограничения CHECK для гарантии того, что будут сохраняться только "хорошие" данные. Например, значения столбцов, создаваемых на основе следующего домена, ограничены символами в верхнем регистре и цифрами:
CREATE DOMAIN TYPECODE CHAR(3)
CHECK(VALUE IS NULL OR VALUE = UPPER(VALUE));
Это замечательно - и мы хотим усилить это правило. Само по себе такое ограничение вызовет исключение, если любое клиентское приложение попытается передать символы в нижнем регистре. С помощью триггера мы можем полностью убрать исключение, исправляя попытки нарушений на месте:
CREATE TRIGGER BA_ATABLE FOR ATABLE
ACTIVE BEFORE INSERT OR UPDATE
AS
BEGIN
NEW.ATYPECODE = UPPER(NEW.ATYPECODE);
END ^
! ! !
ПРИМЕЧАНИЕ. В настоящий момент Firebird поддерживает триггеры только для таблиц и просмотров. Невозможно создать триггер для домена, однако это было бы элегантным улучшением.
. ! .
В определениях доменов и столбцов вы можете указать значение по умолчанию: DEFAULT. Кажется хорошей идеей устанавливать значение столбца, не допускающего значение NULL В некоторое значение по умолчанию, однако SQL-атрибут DEFAULT на деле оказывается беззубым зверем. Он работает только при двух условиях:
* при использовании операции INSERT;
* если этот столбец не включен во входной список оператора.
Поскольку многие современные интерфейсы приложений автоматически создают оператор INSERT, используя выходные столбцы оператора SELECT В качестве входного
списка, то все столбцы помещаются в этот список. Если само приложение не предоставляет значение по умолчанию, то обычным результатом является передача значения NULL. Когда сервер получает значение NULL для такого столбца, он сохраняет в базе данных NULL. Другие ограничения столбца могут вклиниться сюда и вызвать исключение - особенно ограничение NOT NULL - однако значение по умолчанию для столбца никогда не перекроет и не скорректирует любое значение, полученное от клиентского интерфейса.
Вторая проблема связана, конечно, с тем, что значения по умолчанию никогда не применяются, если используется операция изменения.
Короче говоря, триггеры выполняют гораздо более эффективную работу по поддержанию значений по умолчанию, чем это делают атрибуты значения по умолчанию для столбца. Возьмем для примера столбец, основанный на следующем домене:
CREATE DOMAIN MONEY NUMERIC (18, 0)
NOT NULL DEFAULT 0.00;
Триггер BEFORE INSERT OR UPDATE для любого столбца, использующего домен MONEY, реализует значение по умолчанию:
CREATE TRIGGER BI_ACCOUNT FOR ACCOUNT
ACTIVE BEFORE INSERT OR UPDATE
AS
BEGIN
IF (NEW.BALANCE IS NULL) THEN
NEW.BALANCE = 0.00;
END ^
! ! !
СОВЕТ. Вы можете обеспечить поддержание всех значений по умолчанию для таблицы в едином триггерном модуле (версия 1.5 и выше) или в двух параллельных модулях: один для BEFORE INSERT, а другой для BEFORE UPDATE (версия 1.0.x).
. ! .