Лабораторные работы / лабы задания / db_lab_09_of_12_ivt-4
.pdfЛабораторная работа №9
Триггеры, курсоры, транзакции в SQL Server
Цель: работы: получить навыки применения курсоров, использования транзакций, а также работы с триггерами в MS SQL Server.
Для получения теоретических сведений настоятельно рекомендуется при домашней подготовке изучить материалы по тематике лабораторной работы, представленные в открытых источниках.
Далее следует краткий конспект теоретического материала.
Теоретические сведения *
Триггеры
Триггер TRIGGER – процедура, автоматически выполняемая при наступлении заданного события (INSERT, UPDATE, DELETE для триггеров языка манипулирования данными (Data
Manipulation Language — DML), и CREATE, ALTER, DROP для языка определения данных (Data
Definition Language — DDL)).
Создает триггер только владелец базы данных. Это ограничение позволяет избежать случайного изменения структуры таблиц, способов связи с ними других объектов и т.п.
Целями использования триггеров обычно являются:
-проверка корректности введенных данных и выполнение сложных ограничений целостности данных, которые трудно поддерживать с помощью ограничений целостности, установленных для таблицы;
-выдача предупреждений о необходимости выполнения некоторых действий при обновлении таблицы;
-накопление аудиторской информации посредством фиксации сведений о внесенных изменениях и тех лицах, которые их выполнили;
-поддержка репликации.
Создание триггера
Синтаксис:
CREATE TRIGGER имя_триггера
BEFORE | AFTER <триггерное_событие> ON <имя_таблицы>
[REFERENCING <список_старых_или_новых_псевдонимов>]
[FOR EACH { ROW | STATEMENT}] [WHEN(условие_триггера)] <тело_триггера>
ENCRYPTION – тело функции будет недоступно для открытого просмотра
SCHEMABINDING – запрет функции вносить изменения в БД
Кроме того, в MS SQL существуют дополнительные возможности при создании и триггера. В СУБД SQL Server существуют два подкласса триггеров DML: INSTEAD OF (триггер замены операции) и AFTER (триггер, выполняющийся сразу после операции), отличающиеся
своим назначением, моментом выполнения и производимым эффектом (табл. 1).
Синтаксис:
{CREATE | ALTER} TRIGGER имя_триггера
ON {имя_таблицы | имя_представления }
[WITH ENCRYPTION ]
{
{{ FOR | AFTER | INSTEAD OF }
{[ DELETE] [,] [ INSERT] [,] [ UPDATE] } [ WITH APPEND ]
[ NOT FOR REPLICATION ] AS sql_оператор[...n]
} |
{FOR | AFTER | INSTEAD OF } { [INSERT] [,] [UPDATE] } [ WITH APPEND ]
[ NOT FOR REPLICATION ] AS
{IF UPDATE(имя_столбца) [ {AND | OR} UPDATE(имя_столбца)] [...n]
|
IF (COLUMNS_UPDATES() {оператор_бит_обработки} бит_маска_изменения) {оператор_бит_сравнения } бит_маска [...n] }
sql_оператор [...n]
}
}
Пример:
Отменить команду INSERT INTO Сделка …, если в таблице Склад величина остатка товара меньше продаваемого количества товара с введенным кодом:
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
PRINT 'Отмена поставки: товара на складе нет'
END END
Таблица 1. Сравнение характеристик подклассов триггеров DML
Характеристика |
Триггер INSTEAD OF |
Триггер AFTER |
|
Эффект оператора |
Автоматически |
Выполняется, если триггер |
|
DML |
откатывается |
сам не откатит транзакцию |
|
Момент |
Перед проверкой ограничений |
После выполнения транзакции, |
|
выполнения |
|||
первичного и внешнего ключей |
но перед ее подтверждением |
||
триггера |
|||
|
|
||
Количество |
|
|
|
возможных |
Одно |
Несколько |
|
событий таблицы |
|
|
|
Возможность |
|
|
|
применения к |
Есть |
Отсутствует |
|
представлениям |
|
|
|
Рекурсивное |
Отсутствует |
Зависит от параметров |
|
срабатывание |
настройки СУБД |
||
|
Оператор DML не может повлиять на срабатывание триггера, однако триггер можно временно отключить оператором ALTER TABLE с параметром DISABLE TRIGGER:
ALTER TABLE имя_таблицы DISABLE TRIGGER имя_триггера
Для включения триггера применяется тот же оператор, но с параметром ENABLE
TRIGGER:
ALTER TABLE имя_таблицы ENABLE TRIGGER имя_триггера
Триггер AFTER
Триггер AFTER создается оператором следующего вида:
CREATE TRIGGER имя_триггера ON имя_таблицы
AFTER операция, ...
AS
программный_код_триггера
Созданный триггер выполняется в ответ на событие, возникающее после завершения указанной за ключевым словом AFTER операции добавления (Insert), удаления (Delete) или обновления (Update), но до подтверждения фиксации результатов модификации. Например, триггер, созданный оператором:
CREATE TRIGGER ai_PROFIT_trig ON PROFIT AFTER Insert
AS
PRINT('Добавлена строка');
выводит сообщение «Добавлена строка» при добавлении каждой строки в таблицу PROFIT, в чем можно убедиться, если воспользоваться оператором
INSERT INTO PROFIT (Id, Source, Moneys)
VALUES (9, 'Менеджер', 20000.00)
Предложение AFTER
Предложение AFTER позволяет указать операцию (или операции), приводящие к запуску триггера. Может быть предусмотрен запуск триггера при выполнении оператора INSERT, UPDATE или DELETE либо любого сочетания этих трех операторов, например:
AFTER INSERT, DELETE
или
AFTER UPDATE, INSERT
или
AFTER DELETE
Триггеры, объявляемые с предложением AFTER, могут быть закреплены только за таблицами; закрепление этих триггеров за представлениями не допускается.
Триггеры INSERT
Код любого триггера, который объявлен с ключевыми словами AFTER INSERT, вызывается на выполнение каждый раз, когда кто-либо вставляет новую строку в таблицу, за которой закреплен триггер. Для каждой вставляемой строки СУБД SQL Server создает копию этой новой строки и вставляет ее в специальную таблицу, существующую только в области определения данного триггера. Эта таблица называется INSERTED. Наиболее важная особенность таблицы INSERTED состоит в том, что она сохраняется только до тех пор, пока действует триггер, с которым она связана, т.е. таблица INSERTED существует только с момента запуска триггера и до момента завершения его выполнения.
Триггеры DELETE
Триггеры DELETE действуют аналогично триггерам INSERT, не считая того, что связанная с ними таблица INSERTED пуста, вместо этого копия каждой удаленной строки вставляется в таблицу DELETED. Эта таблица, как и таблица INSERTED, имеет область определения, которая ограничивается исключительно продолжительностью существования триггера.
Триггеры UPDATE
Запуск кода триггера, объявленного с ключевыми словами AFTER UPDATE, происходит при каждом внесении изменения в строку, существующую в таблице. При этом в СУБД SQL Server операция модификации каждой строки трактуется так, как будто удаляется существующая строка и вставляется полностью новая. Триггер, объявленный с ключевыми словами AFTER UPDATE, обслуживается с помощью двух специальных таблиц: и INSERTED, и DELETED, количество строк в которых одинаково.
Виртуальные таблицы Inserted и Deleted
Таблицы INSERTED и DELETED можно рассматривать как представления журнала транзакций, т.е. виртуальные таблицы. Их содержимое для каждого оператора DML в краткой форме отражено в табл.2, которая показывает, что таблица DELETED содержит строки в состоянии до применения оператора DML, а таблица INSERTED — после применения. К этим таблицам можно обращаться только внутри триггера; хранимые процедуры, вызываемые триггером, не имеют доступа к этим таблицам.
Таблица 2. Содержимое виртуальных таблиц Inserted и Deleted
Оператор |
|
Виртуальная таблица |
|
Inserted |
|
Deleted |
|
|
|
||
insert |
Вставленные строки |
|
Пустая |
update |
Строки базы данных после |
|
Строки базы данных до обновления |
|
обновления |
|
|
|
|
|
|
delete |
Пустая |
|
Строки, подлежащие удалению |
В следующем примере таблица Inserted используется для создания отчета об изменении места проживания гражданина:
CREATE TRIGGER aiu_PERSON_trig ON Person
AFTER Insert, Update
AS
SET NoCount ON
IF Update(Adr)
SELECT Inserted.FIO + ' изменил место проживания на ' + Inserted.Adr
FROM Inserted;
Пример создания триггера, который при смене должности автоматически увеличивает зарплату:
CREATE TRIGGER Trig_test_1 ON Emp
AFTER UPDATE AS
BEGIN
IF (SELECT id_job FROM Inserted) <> (SELECT id_job FROM Deleted) BEGIN
UPDATE Emp SET Salary=Salary+100 WHERE id_emp= (SELECT id_emp FROM Inserted);
END;
END;
Триггер INSTEAD OF
Триггер INSTEAD OF создается оператором следующего вида:
CREATE TRIGGER имя_триггера ON имя_таблицы
INSTEAD OF операция AS
программный_код_триггера
Созданный триггер выполняется в ответ на событие, возникающее в начале операции, указанной за ключевым словом INSTEAD OF. Триггер INSTEAD OF заменяет транзакцию (т.е. выполняется вместо нее). Это подобно тому, как если бы триггер автоматически откатил операцию, для которой был создан.
Каждая таблица ограничена возможностью использования только одного триггера замены для каждого из своих событий. В то же время триггеры INSTEAD OF могут применяться к представлениям точно так же, как к таблицам.
Триггеры INSTEAD OF обычно используются, в следующих случаях:
-оператор пытается обновить необновляемое представление. В этом случае триггер INSTEAD OF обновляет таблицы, на которых построено данное представление;
-оператор пытается обновить величину, которая должна вычисляться;
-оператор пытается удалить из главной таблицы строку, имеющую связи со строками подчиненной таблицы.
В следующем примере создается и тестируется триггер замены операции вставки:
CREATE TRIGGER ioi_PERSON_trig ON Person INSTEAD OF Insert
AS
PRINT 'Триггер замены вставки' go
INSERT INTO Person (Nom, FIO, Rdate, Pol, SumD, Adr) VALUES(101,'Петров Петр Петрович','16.10.2002','М', 0, 'Зеленоград, 801-1');
В данном случае оператор INSERT сработает так, как будто одна строка была обработана, хотя действие этого оператора блокировано триггером INSTEAD OF. Вместо вставки строки выполняется вывод на экран предусмотренного в триггере сообщения. При этом созданный ранее триггер AFTER остался в силе, однако его сообщение не выведено на экран, поскольку операция вставки не выполнилась.
Приведенный ниже триггер замены операции удаления активизируется при попытке удалить строку из главной таблицы PERSON и предварительно удаляет из подчиненной таблицы HAVE_D связанные строки, а затем строку из главной таблицы:
CREATE TRIGGER iod_PERSON_trig ON Person
INSTEAD OF Delete
AS
DECLARE @x int;
SELECT @x=Deleted.Nom from Deleted;
IF EXISTS (SELECT * from HAVE_D WHERE Nom = @x)
DELETE FROM HAVE_D WHERE Nom = @x;
DELETE FROM PERSON WHERE Nom = @x;
Из используемой в этом триггере виртуальной таблицы DELETED извлекается номер жителя, сведения о котором удаляются, и сохраняется в локальной переменной. Из-за отсутствия рекурсивности сам триггер активизируется только один раз оператором DELETE (например, DELETE FROM PERSON WHERE Nom=5), поэтому выполнение содержащегося в описании триггера оператора DELETE FROM PERSON WHERE Nom = @x происходит единожды.
Курсоры
Курсор CURSOR в SQL – это область в памяти базы данных, которая предназначена для хранения последнего оператора SQL. Если текущий оператор – запрос к базе данных, в памяти сохраняется и строка данных запроса, называемая текущим значением, или текущей строкой курсора. Указанная область в памяти поименована и доступна для прикладных программ.
В каждый момент времени прикладной программой может быть проверена одна строка курсора.
Фактически, курсоры для SQL-запросов аналогичны итераторам для массивов. Основные действия с курсорами:
-создание или объявление курсора;
-открытие курсора (наполнение его данными, которые сохраняются в многоуровневой памяти);
-выборка из курсора и изменение с его помощью строк данных;
-закрытие курсора, после чего он становится недоступным для пользовательских программ;
-освобождение курсора, т.е. удаление курсора как объекта, (закрытие необязательно освобождает ассоциированную с ним память).
Команды управления курсором:
DECLARE – создание или объявление курсора;
OPEN – открытие курсора, т.е. наполнение его данными;
FETCH – выборка из курсора и изменение строк данных с помощью курсора; CLOSE – закрытие курсора;
DEALLOCATE – освобождение курсора, т.е. удаление курсора как объекта.
Объявление курсора
Синтаксис:
DECLARE имя_курсора
[INSENSITIVE][SCROLL] CURSOR FOR SELECT_оператор
[FOR { READ_ONLY | UPDATE [OF имя_столбца[,...n]]}]
INSENSITIVE - статический курсор.
SCROLL - курсор можно.
SELECT-оператор задает тело запроса SELECT, с помощью которого определяется результирующий набор строк курсора.
FOR READ_ONLY - курсор только для чтения.
FOR UPDATE позволяет выполнять в курсоре изменение данных.
Объявление курсора в MS SQL
Синтаксис:
DECLARE имя_курсора CURSOR [LOCAL | GLOBAL]
[FORWARD_ONLY | SCROLL]
[STATIC | KEYSET | DYNAMIC | FAST_FORWARD] [READ_ONLY | SCROLL_LOCKS | OPTIMISTIC] [TYPE_WARNING]
FOR SELECT_оператор
[FOR UPDATE [OF имя_столбца[,...n]]]
LOCAL - курсор виден только в пределах создавшего его пакета, триггера, хранимой процедуры или пользовательской функции.
GLOBAL - существует до закрытия текущего соединения. FORWARD_ONLY - последовательный курсор.
SCROLL - прокручиваемый курсор. STATIC - статический курсор. KEYSET - ключевой курсор. DYNAMIC - динамический курсор.
FAST_FORWARD - оптимизация для быстрого доступа к данным. Не может быть использован совместно с аргументами FORWARD_ONLY и OPTIMISTIC.
OPTIMISTIC - запрещается изменение и удаление строк.
Открытие курсора
Синтаксис:
OPEN {{[GLOBAL]имя_курсора } | @имя_переменной_курсора}
После открытия курсора происходит выполнение связанного с ним оператора SELECT, выходные данные которого сохраняются в памяти.
Выборка из курсора
Синтаксис:
FETCH [[NEXT | PRIOR | FIRST | LAST
| ABSOLUTE {номер_строки | @переменная_номера_строки}
| RELATIVE {номер_строки | @переменная_номера_строки}] FROM ]{{[GLOBAL ]имя_курсора }| @имя_переменной_курсора } [INTO @имя_переменной [,...n]]
FIRST - будет возвращена первая строка полного результирующего набора. LAST - возвращается последняя строка курсора.
NEXT - возвращается строка, находящаяся сразу после текущей. PRIOR - возвращает строку, находящуюся перед текущей.
ABSOLUTE возвращает строку по ее абсолютному порядковому номеру.
RELATIVE возвращает строку, находящуюся через указанное количество строк после текущей.
В конструкции INTO @имя_переменной [,...n] задается список переменных, в которых будут сохранены соответствующие значения столбцов возвращаемой строки. Порядок указания переменных должен соответствовать порядку столбцов в курсоре.
Изменение данных через курсор
UPDATE имя_таблицы SET {имя_столбца={ DEFAULT | NULL | выражение}}[,...n]
WHERE CURRENT OF {{[GLOBAL] имя_курсора} |@имя_переменной_курсора}
За одну операцию могут быть изменены несколько столбцов текущей строки курсора, но все они должны принадлежать одной таблице.
Удаление данных через курсор
DELETE имя_таблицы
WHERE CURRENT OF {{[GLOBAL] имя_курсора}
|@имя_переменной_курсора}
В результате будет удалена строка, установленная текущей в курсоре.
Закрытие курсора
Синтаксис:
CLOSE {имя_курсора | @имя_переменной_курсора}
После закрытия курсор становится недоступным для пользователей программы.
При закрытии снимаются все блокировки, установленные в процессе его работы. Закрытие может применяться только к открытым курсорам.
Закрытый, но не освобожденный курсор может быть повторно открыт. Не допускается закрывать неоткрытый курсор.
Освобождение курсора
Синтаксис:
DEALLOCATE { имя_курсора | @имя_переменной_курсора }
Пример использования курсора:
Использование курсора для вывода списка фирм и клиентов из Москвы:
DECLARE @firm Varchar(50), @fam Varchar(50), @message Varchar(80)
PRINT 'Список клиентов'
DECLARE klient_cursor CURSOR LOCAL FOR
SELECT Фирма, Фамилия FROM Клиент
WHERE Город='Москва'
ORDER BY Фирма, Фамилия
OPEN klient_cursor
FETCH NEXT FROM klient_cursor INTO @firm, @fam
WHILE @@FETCH_STATUS=0
BEGIN
SELECT @message='Клиент '+@fam+' Фирма '+ @firm
PRINT @message
FETCH NEXT FROM klient_cursor INTO @firm, @fam
END
CLOSE klient_cursor
DEALLOCATE klient_cursor
Транзакции
Под транзакцией понимается неделимая с точки зрения воздействия на БД последовательность операторов манипулирования данными (чтения, удаления, вставки, модификации), приводящая к одному из двух возможных результатов:
-либо последовательность выполняется, если все операторы правильные;
-либо вся транзакция откатывается, если хотя бы один оператор не может быть успешно выполнен.
Обработка транзакций гарантирует целостность информации в базе данных. Таким образом, транзакция переводит базу данных из одного целостного состояния в другое.
Управление транзакциями
Под управлением транзакциями понимается способность управлять различными операциями над данными, прежде всего, имеется в виду выполнение операторов INSERT, UPDATE и DELETE.
Существуют три команды, которые используются для управления транзакциями:
-COMMIT – для сохранения изменений;
-ROLLBACK – для отмены изменений;
-SAVEPOINT – для установки особых точек возврата.
Управление транзакциями в среде MS SQL Server
SQL Server поддерживает три вида определения транзакций:
-явное;
-автоматическое;
-подразумеваемое.
По умолчанию SQL Server работает в режиме автоматического начала транзакций, когда каждая команда рассматривается как отдельная транзакция.
Если пользователю нужно создать транзакцию, включающую несколько команд, он должен явно указать транзакцию.
Для установки режима автоматического определения транзакций используется команда:
SET IMPLICIT_TRANSACTIONS OFF
При работе в режиме неявного (подразумевающегося) начала транзакций SQL Server автоматически начинает новую транзакцию, как только завершена предыдущая. Установка режима подразумевающегося определения транзакций выполняется посредством другой команды:
SET IMPLICIT_TRANSACTIONS ON
Явные транзакции
Явные транзакции требуют, чтобы пользователь указал начало и конец транзакции, используя следующие команды:
- начало транзакции:
BEGIN TRAN[SACTION] [имя_транзакции | @имя_переменной_транзакции
[WITH MARK [‘описание_транзакции’]]]
- конец транзакции:
COMMIT [TRAN[SACTION] [имя_транзакции | @имя_переменной_транзакции]]
- создание внутри транзакции точки сохранения (СУБД сохраняет состояние БД в текущей точке и присваивает сохраненному состоянию имя точки сохранения):
SAVE TRAN[SACTION]
{имя_точки_сохранеия | @имя_переменной_точки_сохранения}
- прерывание транзакции (когда сервер встречает эту команду, происходит откат транзакции, восстанавливается первоначальное состояние системы и в журнале транзакций отмечается, что транзакция была отменена):
ROLLBACK [TRAN[SACTION]
[имя_транзакции | @имя_переменной_транзакции | имя_точки_сохранения
|@имя_переменной_точки_сохранения]]