Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
DBM_1_X1.doc
Скачиваний:
2
Добавлен:
14.11.2019
Размер:
5.12 Mб
Скачать

Active after update position 0

AS

BEGIN

IF (OLD.KOD_POKUP <> NEW.KOD_POKUP) THEN

UPDATE RASXOD

SET KOD_POKUP=NEW.KOD_POKUP

WHERE KOD_POKUP = OLD.KOD_POKUP;

END

Триггер – это процедура, которая выполняется автоматически при изменениях, происходящих в таблице. В данном случае триггер будет выполняться после изменений (AFTER UPDATE) в таблице POKUPATELI и выполнит соответствующие изменения в дочерней таблице RASXOD, если изменилось значение родительского ключа (первичного ключа родительской таблицы). Аббревиатура AU в названии триггера построена из символов А(AFTER) и U(UPDATE). OLD.KOD_POKUP и NEW.KOD_POKUP – это старое и новое значения поля KOD_POKUP в таблице POKUPATELI. Перед тем как обновлять таблицу RASXOD триггер проверяет изменилось ли значение поля KOD_POKUP в таблице POKUPATELI. Данная проверка оптимизирует работу сервера (любые изменения в таблице RASXOD связаны с просмотром всей таблицы, что чревато большими накладными расходами при большом объеме таблицы). Заметим, что все SQL-операторы в WINDOWS IB_CONSOLE должны завершаться точкой с запятой.

  • Аналогичный триггер для события удаления записей выглядит следующим образом:

CREATE TRIGGER AD_POKUPATELI FOR POKUPATELI

ACTIVE AFTER DELETE AS

Begin delete from rasxod

WHERE RASXOD.KOD_POKUP = POKUPATELI.KOD_POKUP;

END

Теперь при удалении покупателя или изменении кода покупателя в таблице POKUPATELI соответствующие каскадные изменения произойдут в дочерней таблице RASXOD.

Следует отметить, что таблицы «Товары» и «Расход товара» связаны по формулам:

  • Стоимость купленного товара:= Количество купленного товара * цена единицы товара5;

  • Количество товара на складе:= Количество товара на складе – Количество купленного товара.

Эти соотношения тоже являются требованиями целостности (правильности) базы данных, однако имеют специальный связанный с конкретной предметной областью характер. Контроль первого требования оставим клиентской части программы.

Решение второй задачи возложим на SQL-сервер. Для этого добавим в БД триггер, отрабатывающий после ввода новых данных в таблицу RASXOD

Create trigger ai_rasxod1 for rasxod

ACTIVE AFTER INSERT POSITION 1 AS

BEGIN

UPDATE TOVARY

SET COUNT_TOV = COUNT_TOV-NEW.KOLVO

WHERE KOD_TOVAR = NEW.KOD_TOVAR;

END

Триггер, отрабатывающий перед удалением данных в таблице RASXOD, выглядит следующим образом

CREATE TRIGGER BD_RASXOD1 FOR RASXOD

ACTIVE BEFORE DELETE POSITION 0 AS

BEGIN

UPDATE TOVARY

SET COUNT_TOV= COUNT_TOV+RASXOD.KOLVO

WHERE KOD_TOVAR = RASXOD.KOD_TOVAR;

END

Триггер, отрабатывающий после обновления данных в таблице RASXOD (а точнее при изменении количества купленного товара)

CREATE TRIGGER AU_RASXOD1 FOR RASXOD

ACTIVE AFTER UPDATE POSITION 1 AS

BEGIN

IF ((OLD.KOLVO <> NEW.KOLVO) AND

(OLD.KOD_TOVAR = NEW.KOD_TOVAR)) THEN

UPDATE TOVARY

SET COUNT_TOV= COUNT_TOV+OLD.KOLVO-NEW.KOLVO

WHERE KOD_TOVAR = OLD.KOD_TOVAR;

END;

Остается еще один не рассмотренный случай – изменение кода купленного товара KOD_TOVAR в строке таблицы RASXOD6 должно повлечь перерасчет значений количества товаров на складе COUNT_TOV в таблице TOVARY.

CREATE TRIGGER AU_RASXOD0 FOR RASXOD

ACTIVE AFTER UPDATE POSITION 0 AS

DECLARE VARIABLE OldKod INTEGER;

BEGIN

IF (OLD.KOD_TOVAR <> NEW.KOD_TOVAR) THEN

BEGIN OldKod = NULL;

SELECT KOD_TOVAR FROM TOVARY

WHERE KOD_TOVAR = OLD.KOD_TOVAR

INTO :OldKod;

IF (OldKod IS NOT NULL) THEN

BEGIN

UPDATE TOVARY

SET COUNT_TOV= COUNT_TOV+OLD.KOLVO

WHERE KOD_TOVAR = OLD.KOD_TOVAR;

UPDATE TOVARY

SET COUNT_TOV= COUNT_TOV-NEW.KOLVO

WHERE KOD_TOVAR = NEW.KOD_TOVAR;

END

END

END

Комментарий к триггерам обновления:

  • Вообще-то можно было бы написать один триггер обновления, подходящим образом объединив их содержание. Но раз уж их два, то возникает вопрос об их взаимоотношениях:

  • После события обновления таблицы RASXOD (FOR RASXOD AFTER UPDATE) отработают оба триггера.

  • Порядок, в котором они отработают, задан предложением POSITION n. Правда, в нашем случае этот порядок не важен.

  • Однако важно, чтобы только один из них внес изменения в таблицу TOVARY, поэтому в теле использованы альтернативные IF-условия.

  • В триггере AU_RASXOD0 используется еще одно (внутреннее) IF-условие и связанный с ним оператор SELECT.

Это IF-условие связано со спецификой триггеров – в отличие от обычных процедур триггер вызывается не оператором процедуры, а неявно по соответствующему событию, что достаточно необычно для традиционного процедурного программирования. Поэтому программирование триггеров требует специального внимания – дабы не допустить действий, просто лишних, а тем более искажающих результат или ведущих к порочному кругу (Deadlock).

В нашем случае, если в таблице «Товары» изменится значение поля KOD_TOVAR, то в дочерней таблице «Расход» будет выполнено каскадное обновление согласно объявленному ограничению ссылочной целостности. Это обновление инициирует запуск всех UPDATE-триггеров таблицы RASXOD... для каждой обновляемой её строки... Далее можно просчитать, что такой косвенный вызов не должен повлечь перерасчета количества товаров на складе в таблице «Товары»... а он произойдет и даст неправильный результат...

SELECT-оператор и последующее IF-условие позволяют проверить, имеется ли OLD.KOD_TOVAR в таблице TOVARY... отсутствовать он может только в случае косвенного вызова триггера AU_RASXOD0, инициированного тем самым каскадным обновлением. Возможно, это не самое лучшее решение проблемы, но решение...

Каждая таблица имеет первичный ключ - целочисленное поле. Значения такого поля для различных записей должны быть разными по определению. Поскольку это поле не имеет содержательного смысла и используется только для связи между таблицами, то заполнение этого поля (проверку уникальности его значения) можно поручить серверу базы данных. Для этого удобно использовать генераторы.

Генератор - это переменная, значение которой хранится в БД, поэтому оно не теряется при завершении работы программы и восстанавливается при повторном ее запуске. Создается генератор посредством оператора

CREATE GENERATOR TOVARY_KOD;

Инициализация генератора производится оператором

SET GENERATOR TOVARY_KOD TO 100;

Соответствующие операторы для таблиц POKUPATELI и RASXOD выглядят следующим образом:

CREATE GENERATOR POKUPATELI_KOD;

SET GENERATOR POKUPATELI_KOD TO 100;

CREATE GENERATOR RASXOD_KOD;

SET GENERATOR RASXOD_KOD TO 100;

Далее необходимо определить хранимые процедуры, изменяющие на 1 значения этих переменных.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]