Шрифт:
Интервал:
Закладка:
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: для модификации поведения ограничений ссылочной целостности и для поддержания мета- таблиц (графов), используемых в некоторых иерархических алгоритмах, делающих доступной запросам геометрию дерева. Триггеры для деревьев всегда должны быть спроектированы с условиями и переходами, которые защищают структуру от бесконечных циклов.
Изменение той же строкиНикогда не пытайтесь использовать оператор 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 <тело-триггера>;
ALTER TRIGGERПредложение 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 ^;
ALTER TRIGGER SET_CUST_NO
BEFORE INSERT AS
BEGIN
IF (NEW.CUST_NO IS NULL) THEN
NEW.CUST_NO = GEN_ID(CUST_NO_GEN, 1);
INSERT INTO NEW_CUSTOMERS(NEW.CUST_NO, CURRENT_DATE) END ^
SET TERM ;^
CREATE OR ALTER TRIGGERВ версии 1.5 этот новый синтаксис создает триггер, если триггер с указанным именем не найден, или изменяет триггер с этим именем. Просто отредактируйте исходный оператор CREATE нужным образом, добавив ключевые слова OR ALTER.
Ошибка "объект находится в использовании"Как и в случае с хранимыми процедурами, подтверждение изменений вызовет печально известную ошибку "объект находится в использовании" (Object in use), если в настоящий момент какой-нибудь пользователь использует триггер или какой-либо объект, зависящий от него. В любом случае новая версия триггера не станет немедленно доступной в Суперсервере, если старая версия все еще находится в кэше. Все пользователи должны отключиться от базы данных, а когда они вновь подключатся, то смогут видеть новую версию.
В Классическом сервере новая версия будет доступна следующему клиенту, который соединится с базой данных.
Inactive/ActiveВ версии 1.5 и более поздних выполнение ALTER TRIGGER ... INACTIVE | ACTIVE обычно не приводит к ошибке "объект находится в использовании", если только существующая транзакция не заблокировала таблицу. Такое изменение не влияет на транзакции, которые уже используют эту таблицу. Причем данное изменение будет видимым следующей транзакции, которая запрашивает изменение состояния таблицы.
Удаление триггеров
В процессе проектирования базы данных и разработки приложений триггер может перестать быть полезным. Для удаления триггера соединитесь с базой данных как его владелец или пользователь SYSDBA и используйте оператор DROP TRIGGER.
Его синтаксис:
DROP TRIGGER ИМЯ;
Имя триггера должно быть именем существующего триггера. Следующий пример удаляет триггер SET_CUST_NO:
DROP TRIGGER SET CUST NO;
! ! !
ПРИМЕЧАНИЕ. Чтобы временно сделать триггер недоступным, используйте ALTER TRIGGER имя INACTIVE.
. ! .
Пора дальшеПоследняя глава этой части добавляет сахарную глазурь в наш торт для разработчиков базы данных. Firebird имеет две особенности PSQL: обработка пользовательских исключений и события. С помощью исключений вы имеете весьма детальный контроль над перехватом и обработкой сотен внутренне определенных условий ошибок и множеством ваших собственных исключений. С помощью событий вы можете реализовать механизм обратных вызовов, чтобы проинформировать приложения удаленных клиентов о подтвержденных изменениях, выполненных другими клиентами.
ГЛАВА 32. Обработка ошибок и события.
В этой главе мы рассмотрим, как при выполнении модулей PSQL - триггеров и процедур - можно перехватывать и обрабатывать ошибки в выполняемом коде.
Стандартным поведением модулей PSQL при появлении исключений является остановка выполнения, отмена всей работы, выполненной с начального оператора BEGIN, переход на конечный оператор END и возврат управления клиенту с передачей одного или более сообщений об ошибке. Если этим модулем является триггер, исключение отменит работу всех предыдущих триггеров и предотвратит посылку запрашиваемых изменений DML.
Типы исключений
Может появиться три типа исключений.
* Ошибки SQL - т. е. сообщения SQL, имеющие отрицательное значение SQLCODE.
* Внутренние ошибки Firebird, которые имеют отношение к конкурирующему взаимодействию, данным, метаданным и условиям окружения. У них есть девяти- символьный код ошибки, обычно начинающийся с 3355, который уникально идентифицирует код GDSCODE. Большинство кодов GDSCODE попадают в обобщающие группы кодов SQLCODE, и при возникновении исключения вы обычно получаете и SQLCODE, и GDSCODE.
* Пользовательские исключения, которые вы объявляете как постоянные объекты базы данных и "вызываете" в коде, когда определяется специфическое условие.
- Delphi. Учимся на примерах - Сергей Парижский - Программирование
- Сделай видеоигру один и не свихнись - Слава Грис - Программирование / Руководства
- Психбольница в руках пациентов - Алан Купер - Программирование