
4 Програмування тригера
При виконанні команд додавання, зміни і видалення записів сервер створює дві спеціальні таблиці: inserted і deleted . В них містяться списки рядків, які будуть вставлені або видалені після закінчення транзакції. Структура таблиць inserted і deleted ідентична структурі таблиць, для якої визначається тригер. Для кожного тригера створюється свій комплект таблиць inserted і deleted, тому ніякий інший тригер не зможе дістати до них доступ. Залежно від типу операції, що викликала виконання тригера, вміст таблиць inserted і deleted може бути різним:
-
команда INSERT – в таблиці inserted містяться всі рядки, які користувач намагається вставити в таблицю; в таблиці deleted не буде жодного рядка; після завершення тригера всі рядки з таблиці inserted перемістяться в початкову таблицю;
-
команда DELETE – в таблиці deleted міститимуться всі рядки, які користувач спробує видалити; тригер може перевірити кожний рядок і визначити, чи дозволено її видалення; в таблиці inserted не опиниться жодного рядка;
-
команда UPDATE – при її виконанні в таблиці deleted знаходяться старі значення рядків, які будуть видалені при успішному завершенні тригера. Нові значення рядків містяться в таблиці inserted. Ці рядки додадуться в початкову таблицю після успішного виконання тригера.
Для отримання інформації про кількість рядків, яка буде змінена при успішному завершенні тригера, можна використовувати функцію @@ROWCOUNT; вона повертає кількість рядків, оброблених останньою командою. Слід підкреслити, що тригер запускається не при спробі змінити конкретний рядок, а у момент виконання команди зміни. Одна така команда впливає на безліч рядків, тому тригер повинен обробляти всі ці рядки.
Якщо тригер знайшов, що з 100 рядків, що вставляються, змінних або видаляються, тільки один не задовольняє тим або іншим умовам, то ніякий рядок не буде вставлений, змінений або видалений. Така поведінка обумовлена вимогами транзакції – повинні бути виконані або всі модифікації, або жодної.
Тригер виконується як неявна транзакція, тому усередині тригера допускається використання команд управління транзакціями. Зокрема, при виявленні порушення обмежень цілісності для переривання виконання тригера і відміни всіх змін, які намагався виконати користувач, необхідно використовувати команду ROLLBACK TRANSACTION.
Для отримання списку стовпців, змінених при виконанні команд INSERT або UPDATE, що викликали виконання тригера, можна використовувати функцію COLUMNS_UPDATED(). Вона повертає двійкове число, кожний біт якого, починаючи з молодшим, відповідає одному стовпцю таблиці (в порядку проходження стовпців при створенні таблиці). Якщо біт встановлений в значення "1", то відповідний стовпець був змінений. Крім того, факт зміни стовпця визначає і функція UPDATE (ім’я_колонки).
Для видалення тригера використовується команда
DROP TRIGGER {ім’я_тригера} [,...n]
Приведемо приклади використання тригерів.
Приклад 14.1. Використання тригера для реалізації обмежень на значення. В тій, що додається в таблицю Операція запису кількість проданого товару повинна бути не більше, ніж його залишок з таблиці Склад.
Команда вставки запису в таблицю Операція може бути, наприклад, така:
INSERT INTO Операція
VALUES (3,1, -299,'01/08/2002')
Створюваний тригер повинен відреагувати на її виконання таким чином: необхідно відмінити команду, якщо в таблиці Склад величина залишку товару виявилася кількості товару, що менше продається, з введеним кодом (в прикладі код товара=3). В записі, що вставляється, кількість товару указується із знаком "+", якщо товар поставляється, і із знаком "-", якщо він продається. Представлений тригер налаштований на обробку тільки одному запису, що додається.
CREATE TRIGGER Триггер_ins
ON Операція FOR INSERT
AS
IF @@ROWCOUNT=1
BEGIN
IF NOT EXISTS(SELECT *
FROM inserted
WHERE -inserted.Кількість<=ALL(SELECT
Склад.Залишок
FROM Склад, Операція
WHERE Склад.КодТовара=
Операція.КодТовара))
BEGIN
ROLLBACK TRAN
'Відміна поставки: товару на складі нма'
END
END
Приклад 14.1. Використання тригера для реалізації обмежень на значення.
Приклад 14.2. Використання тригера для збору статистичних даних. Створити тригер для обробки операції вставки запису в таблицю Операція, наприклад, такої команди:
INSERT INTO Операція
VALUES (3,1,200,'01/08/2002')
поставляється товар з кодом 3 від клієнта з кодом 1 в кількості 200 одиниць.
При продажу або отриманні товару необхідно відповідним чином змінити кількість його складського запасу. Якщо товару на складі ще немає, необхідно додати відповідний запис в таблицю Склад. Тригер обробляє тільки один рядок, що додається.
ALTER TRIGGER Триггер_ins
ON Операція FOR INSERT
AS
DECLARE @x INT @y INT
IF @@ROWCOUNT=1
//--в таблицю Операція додається запис про поставку товару
BEGIN
//--кількість проданого товару повинна бути не менше ніж його залишок з таблиці Склад
IF NOT EXISTS(SELECT *
FROM inserted
WHERE -inserted.Кількість< =ALL(SELECT Склад.Залишок
FROM Склад, Операція
WHERE Склад.КодТовара = Операція.КодТовара))
BEGIN
ROLLBACK TRAN
PRINT 'відкат - товару немає '
END
//--якщо запису про поставлений товар ще немає додається відповідний запис в таблицю Склад
IF NOT EXISTS (SELECT *
FROM Склад С, inserted i
WHERE С.КодТовара=i.КодТовара)
INSERT INTO Склад (КодТовара,Залишок)
ELSE
//--якщо запис про товар вже був в таблиці Склад, то визначається код і кількість товару з доданої в таблицю Операція запису
BEGIN
SELECT @y=i.КодТовара @x=i.Кількість
FROM Операція З, inserted i
WHERE С.КодТовара=i.КодТовара
//--і проводиться зміни кількості товару в Таблиці Склад
UPDATE Склад
SET Залишок=Залишок+@x
WHERE КодТовара=@y
END
END
Приклад 14.2. Використання тригера для збору статистичних даних.
Приклад 14.3. Створити тригер для обробки операції видалення запису з таблиці Операція, наприклад, такої команди:
DELETE FROM Операція WHERE КодСделки=4
Для товару, код якого вказаний при видаленні запису, необхідно відкоректувати його залишок на складі. Тригер обробляє тільки один запис, що видаляється.
CREATE TRIGGER Триггер_del
ON Операція FOR DELETE
AS
IF @@ROWCOUNT=1 //-- видалений один запис
BEGIN
DECLARE @y INT,@x INT
//--визначається код і кількість товару з видаленої з таблиці Склад запису
SELECT @y=КодТовара @x=Кількість
FROM deleted
//--в таблиці Склад коректується кількість Товару
UPDATE Склад
SET Залишок=Залишок-@x
WHERE КодТовара=@y
END
Приклад 14.3. Тригер для обробки операції видалення запису з таблиці
Приклад 14.4. Створити тригер для обробки операції зміни запису в таблиці Операція, наприклад, такою командою:
UPDATE Операція SET Кількість=Кількість-10
WHERE КодТовара=3
у всіх операціях з товаром, що має код, рівний 3, зменшити кількість товару на 10 одиниць.
Вказана команда може привести до зміни відразу декількох записів в таблиці Операція. Тому покажемо, як створити тригер, оброблювальний не один запис. Для кожного зміненого запису необхідно для старого (до зміни) коду товару зменшити залишок товару на складі на величину старої (до зміни) кількості товару і для нового (після зміни) коду товару збільшити його залишок на складі на величину нового (після зміни) значення. Щоб обробити всі змінені записи, введемо курсори, в яких збережемо всі старі (з таблиці deleted) і всі нові значення (з таблиці inserted).
CREATE TRIGGER Триггер_upd
ON Операція FOR UPDATE
AS
DECLARE @x INT @x_old INT @y INT @y_old INT
//-- курсор з новими значеннями
DECLARE CUR1 CURSOR FOR
SELECT КодТовара, Кількість
FROM inserted
//-- курсор із старими значеннями
DECLARE CUR2 CURSOR FOR
SELECT КодТовара, Кількість
FROM deleted
OPEN CUR1
OPEN CUR2
//-- переміщаємося паралельно по обох курсорах
FETCH NEXT FROM CUR1 INTO @x @y
FETCH NEXT FROM CUR2 INTO @x_old @y_old
WHILE @@FETCH_STATUS=0
BEGIN
//--для старого коду товару зменшується його --кількість на складі
UPDATE Склад
SET Залишок = Залишок-@y_old
WHERE КодТовара=@x_old
//--для нового коду товару, якщо такого товару ще немає на складі, вводиться новий запис
IF NOT EXISTS (SELECT * FROM Склад
WHERE КодТовара=@x)
INSERT INTO Склад(КодТовара,Залишок)
VALUES (@x,@y)
ELSE
//--інакше для нового коду товару збільшується --його кількість на складі
UPDATE Склад
SET Залишок = Залишок+@y
WHERE КодТовара=@x
FETCH NEXT FROM CUR1 INTO @x @y
FETCH NEXT FROM CUR2 INTO @x_old @y_old
END
CLOSE CUR1
CLOSE CUR2
DEALLOCATE CUR1
DEALLOCATE CUR2
Приклад 14.4. тригер для обробки операції зміни запису в таблиці
В розглянутому тригері відсутнє порівняння кількості товару при зміні запису про операцію з його залишком на складі.
Приклад 14.5. Поправний цей недолік. Для генерації повідомлення про помилку використовуємо в тілі тригера команду MS SQL Server RAISERROR, аргументами якої є текст повідомлення, рівень серйозності і статус помилки.
ALTER TRIGGER Триггер_upd
ON Операція FOR UPDATE
AS
DECLARE @x INT @x_old INT @y INT
@y_old INT,@o INT
DECLARE CUR1 CURSOR FOR
SELECT КодТовара,Кількість
FROM inserted
DECLARE CUR2 CURSOR FOR
SELECT КодТовара,Кількість
FROM deleted
OPEN CUR1
OPEN CUR2
FETCH NEXT FROM CUR1 INTO @x @y
FETCH NEXT FROM CUR2 INTO @x_old @y_old
WHILE @@FETCH_STATUS=0
BEGIN
SELECT @o=Залишок
FROM Склад
WHERE кодтовара=@x
IF @o<-@y
BEGIN
RAISERROR('откат',16,10)
CLOSE CUR1
CLOSE CUR2
DEALLOCATE CUR1
DEALLOCATE CUR2
ROLLBACK TRAN
RETURN
END
UPDATE Склад
SET Залишок=Залишок-@y_old
WHERE КодТовара=@x_old
IF NOT EXISTS (SELECT * FROM Склад
WHERE КодТовара=@x)
INSERT INTO Склад(КодТовара, Залишок)
VALUES (@x,@y)
ELSE
UPDATE Склад
SET Залишок=Залишок+@y
WHERE КодТовара=@x
FETCH NEXT FROM CUR1 INTO @x @y
FETCH NEXT FROM CUR2 INTO @x_old @y_old
END
CLOSE CUR1
CLOSE CUR2
DEALLOCATE CUR1
DEALLOCATE CUR2
Приклад 14.5. Виправлений варіант тригера для обробки операції зміни запису в таблиці
Приклад 14.6. В прикладі 14.5 відбувається відміна всіх змін при неможливості реалізувати хоча б одне з них. Створимо тригер, що дозволяє відміняти зміну тільки деяких записів і виконувати зміну інших.
В цьому випадку тригер виконується не після зміни записів, а замість команди зміни.
ALTER TRIGGER Триггер_upd
ON Операція INSTEAD UPDATE
AS
DECLARE @k INT @k_old INT
DECLARE @x INT @x_old INT @y INT
DECLARE @y_old INT,@o INT
DECLARE CUR1 CURSOR FOR
SELECT КодСделки, КодТовара, Кількість
FROM inserted
DECLARE CUR2 CURSOR FOR
SELECT КодСделки, КодТовара,Кількість
FROM deleted
OPEN CUR1
OPEN CUR2
FETCH NEXT FROM CUR1 INTO @k,@x @y
FETCH NEXT FROM CUR2 INTO @k_old,@x_old
@y_old
WHILE @@FETCH_STATUS=0
BEGIN
SELECT @o=Залишок
FROM Склад
WHERE КодТовара=@x
IF @o>=-@y
BEGIN
RAISERROR('изменение',16,10)
UPDATE Операція SET Кількість=@y
КодТовара=@x
WHERE КодСделки=@k
UPDATE Склад
SET Залишок=Залишок-@y_old
WHERE КодТовара=@x_old
IF NOT EXISTS (SELECT * FROM Склад
WHERE КодТовара=@x)
INSERT INTO Склад(КодТовара, Залишок)
VALUES (@x,@y)
ELSE
UPDATE Склад
SET Залишок=Залишок+@y
WHERE КодТовара=@x
END
ELSE
RAISERROR('запис не змінено',16,10)
FETCH NEXT FROM CUR1 INTO @k,@x @y
FETCH NEXT FROM CUR2 INTO @k_old,@x_old
@y_old
END
CLOSE CUR1
CLOSE CUR2
DEALLOCATE CUR1
DEALLOCATE CUR2
Приклад 14.6. Тригер, що дозволяє відміняти зміну тільки деяких записів і виконувати зміну інших.
Контрольні питання
-
Дайте визачення тригера. В чому полягає його призначення?
-
Хто може створити тригер? Чому?
-
Яких переваг використання можна досягти за допомогою тригера?
-
Наведіть формальний синтаксис створення тригера та поясніть його параметри.
-
Що таке подія тригера? На що вона впливає?
-
В чому полягають недоліки тригерів?
-
Перерахуйте відомі вам типи тригерів.
-
Що представляє собою AFTER-тригер?
-
Що представляє собою INSTEAD-тригер?
-
Яку функцію виконує INSERT TRIGGER?
-
Яку функцію виконує UPDATE TRIGGER?
-
Яку функцію виконує DELETE TRIGGER?
-
В чому полягає призначення таблиць inserted і deleted?