Практическая работа
Создание генератора и триггеров. Каскадные воздействия
Создание генераторов
1. Вызовите SQL Explorer и откройте БД IB_BIBL_ХХ. Выполните такой запрос: CREATE GENERATOR GEN_NAKLS
2. К сожалению, в операторе SET GENERATOR начальное значение не может быть результатом запроса SELECT. Поэтому сначала с помощью следующего оператора получите (и запомните) очередное значение, которое нужно поместить в столбец NaklID таблицы NAKLS:
SELECT MAX(NaklID)+1 FROM Nakls
3. Далее установите начальное значение генератора:
SET GENERATOR Gen_Nakls TO число
Здесь число — значение, полученное предыдущим оператором SELECT.
4. Создайте таким же образом генераторы GEN_BOOKS, GEN_FIRMS, GEN_ PAYMENTS и GEN_MOVEOOK для соответствующих таблиц.
Создание триггеров
5. С помощью триггеров можно установить в автоинкрементное поле значение, полученное от генератора. Создайте такой триггер:
CREATE TRIGGER BEF_INS_NAKLS FOR Nakls
ACTIVE BEFORE INSERT
AS
BEGIN
NEW. NakllD = GEN_ID(GEN_NAKLS,1);
END
В первой строке помимо имени триггера BEF_INS_NAKLS (хотя триггеры нельзя вызвать обращением к ним по имени в хранимых процедурах или SQL-запросах, они, как и все другие сущности серверной БД, должны иметь уникальные имена) указывается также таблица Nakls, для которой он предназначен. Следующая строка указывает, когда триггер должен активизироваться (в нашем случае — перед вставкой новой записи). Третья строка содержит зарезервированное слово AS, которым открывается тело триггера. Тело триггера всегда (даже если триггер содержит единственный оператор, как в нашем случае) должно ограничиваться парой ключевых слов BEGIN и END. В пятой строке расположен единственный исполняемый оператор, в котором новому значению (предикат NEW) поля NakllD присваивается значение, полученное от встроенной функции GEN_ID. Двумя параметрами обращения к этой функции указывается имя генератора и то значение, на которое должно увеличиться текущее значение генератора («шаг» генератора). В триггерах INSERT и UPDATE можно обращаться к новому значению поля, если его имени предшествует предикат NEW, а в триггерах DELETE и UPDATE — к старому значению (предикат OLD).
ВНИМАНИЕ Инициировать автоинкрементные поля с помощью генераторов можно только в триггерах, срабатывающих перед вставкой новой записи.
6. Аналогичным образом создайте триггеры BEF_INS_FIRMS, BEF_INS_MOVEB BEF_INS_BOOKS, BEF_INS_PAYMENTS для остальных автоинкрементных полей.
7. Создайте триггер, который перед удалением данных о накладной удаляет данные обо всех связанных с ней книгах:
CREATЕ TRIGGER BEFORE_DEL_NAKLS FOR NAKLS ACTIVE BEFORE DELETE AS
BEGIN
DELETE FROM MoveBook WHERE MNakl = NakllD;
END
8. Удаление данных об отдельной книге влечет за собой более сложную логику действий: на основании типа накладной необходимо скорректировать количество экземпляров книги на складе и сальдо партнера, а также изменить сумму накладной:
CREATE TRIGGER BEFORE_DEL_MOVEBOOK FOR MOVEBOOK ACTIVE BEFORE DELETE
AS
/* объявление вспомогательных переменных: */
DECLARE VARIABLE TypeN INTEGER; /* Тип накладной */
DECLARE VARIABLE FirmN INTEGER; /* Шифр партнера */
DECLARE VARIBLE Coeff FLOAT; /* Коэффициент накладной */
BEGIN
/* Помещаем тип накладной в переменную TypeN шифр партнера в
переменную FirmN и коэффициент в Coeff */
SELECT NType, NFirm FROM Nakls WHERE Nakls. NaklID=OLD. MNakl INTO :TypeN, :FirmN;
IF (:TypeN IN (0,3,4,6)) THEN
BEGIN
/* Удаляется книга из накладной, связанной с приходом книг.
Уменьшаем количество книг на складе */
UPDATE Books
SET BQuan=BQuan-OLD. MQuan
WHERE BookID=OLD. MBook;
/* Корректируем сальдо партнера */
IF (:TypeN=4) THEN
/* Увеличиваем обменное сальдо */
UPDATE Firms
SET FChgDelta = FChgDelta+OLD. MQuan*OLD. MPrice*:Coeff
WHERE FirmID=:FirmN; ELSE
/* Увеличиваем финансовое сальдо */
UPDATE Firms
SET FFinDelta = FFinDelta+OLD. MQuan*OLD. MPrice*:Coeff
WHERE FirmID=:FirmN;
END
ELSE
BEGIN
/*Удаляется книга из накладной, связанной с уходом книг. Увеличиваем количество книг на складе */
UPDATE Books SET BQuan=BQuan+OLD. MQuan
WHERE BookID=OLD. MBook;
IF (:TypeN=4) THEN
/*Уменьшаем обменное сальдо */
UPDATE Firms
SET FChgDelta = FChgDelta+OLD. MQuan*OLD. MPrice*:Coeff
WHERE FirmID=:FirmN;
ELSE
/*Уменьшаем финансовое сальдо */
UPDATE Firms
SET FFinDelta= FFinDelta+OLD. MQuan*OLD. MPrice*:Coeff
WHERE FirmID=:FirmN; END END
Многочисленные комментарии поясняют особенности тех или иных действий.
Как видите, в триггерах допускаются локальные переменные и условные операторы. Обратите внимание на следующую деталь. Триггер BEFORE_DEL_MOVEBOOK имеет серьезный недостаток: он не корректирует сумму накладной. Сделано это не случайно. Можно, например, в его начале вставить такие строки: UPDATE Nakls
SET NSum = NSum-OLD. MQuan*OLD. MPrice*:Coeff WHERE NaklID=OLD. MNakl;
Если это сделать, то триггер прекрасно справится с задачей удаления данных о книге из накладной, но попытка удаления всей информации по накладной окажется невозможной, так как триггер будет пытаться изменить поле в удаляемой накладной. Как решить эту задачу? Дело в том, что в отличие от процедуры триггеру нельзя передать параметр, который бы указывал на то, что удаляется информация по накладной в целом. Получив такой параметр, триггер просто не стал бы изменять сумму накладной, да и коррекцию сальдо партнера можно было бы в этом случае сделать в триггере BEF_DEL_NAKLS сразу для всей суммы, а не делать этого для каждой отдельной книги. Однако повторяю, триггеру нельзя передать параметр, а в БД не существует глобальных переменных. Когда я впервые столкнулся с подобной проблемой на практике, она показалась мне неразрешимой. Однако после некоторого раздумья я нашел, как мне кажется, вполне приемлемое решение. Я добавил в таблицу MOVEBOOK новое поле: ALTER TABLE MOVEBOOK ADD IsDelNakl VARCHAR(l)
Это поле заполняется в процессе ввода значением F в триггере BEF_INS_MOVEBOOK:
CREATE TRIGGER BEF_INS_MOVEBOOK FOR MOVEBOOK BEFORE INSERT AS
BEGIN
NEW. IsDelNakl="F";
NEW. MoveId=GEN_ID(GEN_MOVEBOOK,1);
END
Триггер BEF_DEL_NAKL первым делом выполняет такой оператор:
UPDATE MoveBook SET IsDelNakl="T" WHERE MNakl=OLD. NaklID;
Теперь триггер BEF_DEL_MOVEBOOK может проанализировать это поле и блокировать коррекцию суммы в накладной, если поле содержит Т: IF (OLD. IsDelNakl <> "Т") THEN
UPDATE Nakls
SET NSum = NSum-OLD. MQuan*OLD. MPrice*:Coeff
WHERE NaklID=OLD. MNakl;
Правильные тексты триггеров можно увидеть с помощью SQL Explorer. Для этого раскройте узел рядом с нужной таблицей, затем раскройте узел Triggers, выделите название нужного триггера и перейдите на вкладку Text.
Основные порталы (построено редакторами)
