Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
4 курс (заочка) / Лабораторные работы / !Лабораторный практикум ТБД (задание).pdf
Скачиваний:
17
Добавлен:
08.01.2022
Размер:
1.12 Mб
Скачать

Лабораторная работа №7-8

Триггеры

Триггеры в Oracle — это процедуры на языке Java или SQL, которые вызываются при выполнении определенных действий с базой данных. Oracle поддерживает несколько типов триггеров: одни запускаются командами SQL, создающими в базе данных новые структуры, например таблицы, другие запускаются единожды на уровне таблицы, когда происходит изменение строк таблицы, третьи запускаются по одному разу для каждой измененной строки. Последние называются строчными триггерами (row triggers); их мы и будем рассматривать здесь. Oracle поддерживает три вида триггеров: предваряющие (триггеры BEFORE), завершающие (триггеры AFTER) и замещающие (триггеры INSTEAD OF). Рассмотрим примеры каждого из этих типов.

Пример предваряющего триггера

Влистинге 1 показан предваряющий триггер, который используется для задания значения строки перед обновлением.

Чтобы использовать этот триггер, создайте сначала в таблице

TRANSACTION новый столбец под именем AskingPrice типа Number(7, 2).

Влистинге 1 триггер New_Price определен фразой

Before Insert or Update of AcquisitionPrice ON TRANSACTION.

Эта фраза, являясь правильной, содержит неопределенность. Она означает «ПЕРЕД (любой) вставкой в транзакции или ПЕРЕД обновлением AcquisitionPrice в транзакции запустить триггер». Таким образом, триггер будет запущен при любой вставке или при обновлении столбца AcquisitionPrice. Обновления других столбцов не вызовут запуска триггера.

Листинг 1. Предваряющий триггер New_Price CREATE OR REPLACE TRIGGER New_Price

BEFORE INSERT OR UPDATE of Acquisitionprice ON TRANSACTION

FOR EACH ROW

/* Устанавливаем новое значение AskingPrice перед вставкой или обновлением */

42

BEGIN

:new.AskingPrice := :new.AcquistionPrice * 2;

END;

Фраза FOR EACH ROW (для каждой строки) указывает, что данный триггер является строчным. Логика его работы проста: новое значение AskingPrice вычисляется как удвоенное новое значение AcquisitionPrice.

Префикс new доступен только для триггеров. Он обозначает новое значение столбца в команде INSERT или UPDATE. Так, new.AcquisitionPrice обозначает новое значение AcquisitionPrice (заданное пользователем). Для обновлений и удалений существую также префикс old, обозначающий значение столбца до выполнения команды UPDATE или DELETE.

Создайте триггер в файле Trig1.sql. Запустите файл в SQL Plus - триггер скомпилируется. Если не будет ошибок компиляции, триггер будет сохранен в базе данных и станет активным. Обновите столбец AcquisitionPrice и затем выберите строки, которые вы обновили. Значение столбца AskingPrice будет задано триггером.

Если у вас будут ошибки компиляции, вы сможете их увидеть, введя команду Show Errors, как это делалось ранее с хранимыми процедурами.

Пример завершающего триггера

В листинге 2 показан пример завершающего триггера. Логика его такова: View Ridge определяет произведение, предназначенное для продажи, как любое произведение, имеющее строку в таблице TRANSACTION с пустым значением CustomerID. Задача этого триггера — гарантировать, что такая строка в таблице TRANSACTION будет существовать, когда в базу данных будет записываться произведение. Этот триггер, запускаемый при создании строки в таблице WORK, проверяет таблицу TRANSACTION и не производит никаких действий, если находит в ней подходящую строку.

Листинг 2. Завершающий триггер On_Work_lnsert

CREATE OR REPLACE TRIGGER On_WORK_Insert AFTER INSERT ON WORK

FOR EACH ROW

43

DECLARE

rowcount integer(2); BEGIN

/* Считаем доступные строки */

SELECT

Count (*) INTO rowcount

FROM

TRANSACTION

WHERE

CustomerID IS NULL AND WorkID5:new.WorkID;

IF rowcoiint > 0 Then /* Строка существует, ничего не предпринимаем */

DBMS_OUTPUT.PUT_LINE ('Подходящая строка в таблице TRANSACTION существует -- никаких действий не предпринято.');

RETURN;

ELSE /* Нужно добавить новую строку */

INSERT INTO TRANSACTION (TransactionID. DateAcquired, WorkID)

VALUES (TransID.NextVal SysDate. :new.WorkID): END IF;

END;

/

Логика работы триггера элементарна. Он подсчитывает количество подходящих строк, и если это количество больше нуля, то ничего не предпринимается;

если оно равно нулю, в таблице TRANSACTION создается новая строка с соответствующими данными. Обратите внимание на то, как префикс :new используется в части VALUES оператора INSERT.

Пример замещающего триггера

Замещающие триггеры используются для обновления представлений, Рассмотрим представление, определенное в листинге 3. Оно соединяет в себе

44

четыре таблицы и, как представление соединения, не может быть обновлено без помощи соответствующего триггера.

Листинг 3. Определение представления CustomerPurchases CREATE VIEM CustomerPurchases AS

SELECT

CUSTOMER.Name CustName, Copy, Title, ARTIST.Name

ArtistName

 

FROM

CUSTOMER, TRANSACTION, WORK, ARTIST

WHERE

CUSTOMER.CustomerID = TRANSACTION.CustomerID AND

TRANSACTION.WorkID = WORK.WorkID AND

WORK.ArtistID = ARTIST.ArtiStID;

Создать представление и вывести содержащиеся в нем данные

Обратите внимание на фразу SELECT в определении этого представления. Она задает синонимы для столбцов CUSTOMER.Name (синоним CustName) и ARTIST.Name (синоним ArtistName). Если бы это не было сделано, в представлении оказалось бы одна столбца с именем Name, а такое недопустимо.

Исследуйте данные выведенные в представлении; подумайте, что должно произойти, когда пользователь попытается обновить столбец Title. Название «Mystic Fabric» фигурирует в трех строках, но в таблице WORK имеется только две строки с таким названием. Что должна сделать Oracle, когда пользователь обновит столбец Title? Должна ли она обновить строки в таблице WORK, на котором основано это представление? Или она должна создать в таблице WORK новые строки и связать их со строками данного представления? А может, необходимо сделать что-то другое?

Невозможно написать для СУБД обобщенный код, который бы корректно обрабатывал каждую возможную ситуацию.

Замещающие триггеры позволяют разработчику задать действия, которые необходимо предпринять при попытке обновить представление в конкретном приложении. В листинге 4 показан пример такого триггера, который обрабатывает обновления столбца Title представления CustomerPurchases.

45

Поскольку замещающие триггеры, в отличие от предваряющих, не могут содержать фразу UPDATE OF, мы должны написать код, определяющий, был ли изменен столбец Title.

Листинг 4. Замещающий триггер Title_Update

CREATE OR REPLACE TRIGGER Title_Update INSTEAD OF UPDATE ON CustomerPurchases

FOR EACH ROW

BEGIN

/* Ничего не делаем, кроме случая, когда обновляется столбец Title

*/

IF :new.Title 5 :old.Title THEN RETURN;

END IF;

UPDATE

WORK

SET Title = :new.Tit1e

WHERE

Title = :old.Title;

END;

Если пользователь обновляет столбец Title, то соответствующее изменение вносится в таблицу WORK. Обратите внимание, что запуск триггера происходит при обновлении представления CustomerPurchases, но само обновление производится в таблице WORK — одной из таблиц, на которой базируется это представление. Как раз для таких действий и предназначаются замещающие триггеры,

Этот триггер может приводить к неожиданным результатам. Если пользователь введет:

UPDATE CustomerPurchases SET Title = 'аа'. Copy = '1/3' WHERE Title = 'bb';

то будет произведено обновление столбца Title, но ничего не произойдет со столбцом Copy. Лучше было бы выдать пользователю сообщение, предупреждающее о таком эффекте.

46