- •1.База данных (условной) предметной области.
- •2.Создание базы данных. Работа с InterBase windows ib_console.
- •Active after update position 0
- •Begin delete from rasxod
- •Create trigger ai_rasxod1 for rasxod
- •Create procedure get_kod_tovar
- •Create procedure get_kod_pokup
- •Create procedure get_kod_rasxod
- •Violation of primary or unique key constraint "integ_4" on table "tovary"
- •Violation of foreign key constraint "tov_rash" on table "rasxod"
- •Violation of foreign key constraint "pok_rash" on table "rasxod"
- •3.Администрирование базы данных. Bde. Создание псевдонима.
- •4.Разработка клиентского места
- •4.1 Основная экранная форма
- •If not RasxodTable.Eof then RasxodTable.Next
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 значения этих переменных.