Архив / Пособие_Триггеры и процедуры (1)
.pdfПетрГУ, кафедра прикладной математики и кибернетики
Если триггер определен как триггер, вызываемый после выполнения операции Update, то триггер будет работать следующим образом:
1.Пользователь передает СУБД команду Update;
2.СУБД записывает новые значения записи из переменной NEW в обновляемую
таблицу;
3.СУБД заполняет переменные OLD и NEW;
4.СУБД вызывает триггер.
Если триггер определен как триггер, вызываемый до выполнения операции Delete, то триггер будет работать следующим образом:
1.Пользователь передает СУБД команду Delete;
2.СУБД заполняет переменную OLD;
3.СУБД вызывает триггер;
4.СУБД удаляет запись из таблицы.
Если триггер определен как триггер, вызываемый после выполнения операции Delete, то триггер будет работать следующим образом:
1.Пользователь передает СУБД команду Delete;
2.СУБД удаляет запись из таблицы;
3.СУБД заполняет переменную OLD;
4.СУБД вызывает триггер.
Удаление
DROP TRIGGER <имя триггера>
Конспект лекций по дисциплине «Базы данных» (Процедуры и триггеры) |
11 |
ПетрГУ, кафедра прикладной математики и кибернетики
6 Примеры реализации триггеров
Пример 1
Ограничение предметной области: стипендия студента не может быть увеличена более чем на 5% от предыдущей стипендии.
CREATE TRIGGER tgrStudentGrantUpdate
BEFORE UPDATE OF Grant
ON tblStudent
BEGIN
IF (NEW.Grant – OLD.Grant > 0.05 * OLD.Grant)
SET NEW.Grant = 1.05 * OLD.Grant
END
Триггер tgrStudentGrantUpdate создан для таблицы tblStudent. Триггер будет срабатывать до выполнения операции изменения данных, чтобы в таблицу были записаны уже корректные данные.
При вызове триггера СУБД создает две переменных: OLD, содержащую значения изменяемой записи до ее изменения, и NEW, содержащую значения изменяемой записи после ее изменения.
После вызова триггера в теле триггера проверяется условие о величине изменения стипендии. Если стипендия изменилась более чем на 5%, то триггер вносит поправку в данные – увеличивает стипендию только на 5% по сравнению с предыдущим значением стипендии студента. Это действие выполняется посредством записи правильного значения стипендии в переменную NEW.
Рассмотрим более детально работу триггера.
Пусть таблица tblStudent включает следующие записи: tblStudent
StudentId |
StudentName |
Grant |
… |
1023 |
Бобров А.А. |
10000 |
… |
1025 |
Мухина В.Н. |
12000 |
… |
1026 |
Лобов А.Г. |
14000 |
… |
Пусть пользователь отправил на выполнение следующую команду:
Update tblStudent Set Grant = 12000 Where StudentId = 1023
Прежде, чем выполнить эту команду СУБД вызовет триггер tgrStudentGrantUpdate. При этом СУБД создаст переменные OLD и NEW и наполнит их следующими
данными:
OLD
|
StudentId |
StudentName |
Grant |
|
… |
|
|
1023 |
Бобров А.А. |
10000 |
|
… |
|
|
|
|
|
|
|
|
Конспект лекций по дисциплине «Базы данных» (Процедуры и триггеры) |
12 |
ПетрГУ, кафедра прикладной математики и кибернетики
NEW
StudentId |
StudentName |
Grant |
… |
1023 |
Бобров А.А. |
12000 |
… |
Проверка условия дает истинное значение, и триггер в переменной NEW изменяет значение в поле Grant:
NEW
StudentId |
StudentName |
Grant |
… |
1023 |
Бобров А.А. |
10500 |
… |
Триггер заканчивает работу, и СУБД вносит изменения в таблицу для записи с идентификатором 1023, выбирая новые значения для записи из переменной NEW.
В результате в таблице tblStudent записи будут такими:
tblStudent |
|
|
|
|
|
|
|
|
|
|
StudentId |
StudentName |
Grant |
… |
|
1023 |
Бобров А.А. |
10500 |
… |
|
1025 |
Мухина В.Н. |
12000 |
… |
|
1026 |
Лобов А.Г. |
14000 |
… |
Рассмотрим другую ситуацию. |
|
|
||
Пусть таблица tblStudent включает следующие записи: |
|
|||
tblStudent |
|
|
|
|
|
|
|
|
|
|
StudentId |
StudentName |
Grant |
… |
|
1023 |
Бобров А.А. |
10000 |
… |
|
1025 |
Мухина В.Н. |
12000 |
… |
|
1026 |
Лобов А.Г. |
14000 |
… |
Пусть пользователь отправил на выполнение следующую команду:
Update tblStudent Set Grant = 18000
Эта команда отличается от предыдущей тем, что в результате ее выполнения изменению подвергнутся все три записи таблицы tblStudent. Как же сработает триггер в этой ситуации?
Как уже говорилось ранее, триггер будет вызван отдельно для каждой записи. Пусть записи обрабатываются в порядке возрастания идентификаторов.
СУБД начнет обрабатывать первую запись с идентификатором 1023, прежде, чем внести изменения, СУБД вызовет триггер и заполнит переменные OLD и NEW:
OLD
|
StudentId |
|
StudentName |
|
Grant |
|
… |
|
|
|
|
|
|
||||
|
1023 |
|
Бобров А.А. |
|
10000 |
|
… |
|
NEW |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
StudentId |
|
StudentName |
|
Grant |
|
… |
|
|
1023 |
|
Бобров А.А. |
|
18000 |
|
… |
|
|
|
|
|
|
|
|
|
|
Конспект лекций по дисциплине «Базы данных» (Процедуры и триггеры) |
13 |
ПетрГУ, кафедра прикладной математики и кибернетики
Триггер внесет изменения в переменную NEW, так как для этой записи ограничения предметной области не выполняются:
OLD
|
StudentId |
StudentName |
Grant |
… |
|
1023 |
Бобров А.А. |
10000 |
… |
NEW |
|
|
|
|
|
|
|
|
|
|
StudentId |
StudentName |
Grant |
… |
|
1023 |
Бобров А.А. |
10500 |
… |
Затем СУБД внесет изменения в таблицу tblStudent. tblStudent
StudentId |
StudentName |
Grant |
… |
1023 |
Бобров А.А. |
10500 |
… |
1025 |
Мухина В.Н. |
12000 |
… |
1026 |
Лобов А.Г. |
14000 |
… |
Далее СУБД перейдет к обработке второй записи с идентификатором 1025, прежде, чем внести изменения, СУБД вызовет триггер и заполнит переменные OLD и NEW:
OLD
|
StudentId |
StudentName |
Grant |
… |
|
1025 |
Мухина В.Н. |
12000 |
… |
NEW |
|
|
|
|
|
|
|
|
|
|
StudentId |
StudentName |
Grant |
… |
|
1025 |
Мухина В.Н. |
18000 |
… |
Триггер внесет изменения в переменную NEW, так как для этой записи ограничения предметной области не выполняются:
OLD
|
StudentId |
StudentName |
Grant |
… |
|
1025 |
Мухина В.Н. |
12000 |
… |
NEW |
|
|
|
|
|
|
|
|
|
|
StudentId |
StudentName |
Grant |
… |
|
1025 |
Мухина В.Н. |
12600 |
… |
Затем СУБД внесет изменения в таблицу tblStudent. tblStudent
|
StudentId |
StudentName |
Grant |
|
… |
|
|
1023 |
Бобров А.А. |
10500 |
|
… |
|
|
1025 |
Мухина В.Н. |
12600 |
|
… |
|
|
1026 |
Лобов А.Г. |
14000 |
|
… |
|
|
|
|
|
|
|
|
Конспект лекций по дисциплине «Базы данных» (Процедуры и триггеры) |
14 |
ПетрГУ, кафедра прикладной математики и кибернетики
Далее СУБД перейдет к обработке третей записи с идентификатором 1026, прежде, чем внести изменения, СУБД вызовет триггер и заполнит переменные OLD и NEW:
OLD
|
StudentId |
StudentName |
Grant |
… |
|
1026 |
Лобов А.Г. |
14000 |
… |
NEW |
|
|
|
|
|
|
|
|
|
|
StudentId |
StudentName |
Grant |
… |
|
1026 |
Лобов А.Г. |
18000 |
… |
Триггер внесет изменения в переменную NEW, так как для этой записи ограничения предметной области не выполняются:
OLD
|
StudentId |
StudentName |
Grant |
… |
|
|
1026 |
Лобов А.Г. |
14000 |
… |
|
NEW |
|
|
|
|
|
|
|
|
|
|
|
|
StudentId |
StudentName |
Grant |
… |
|
|
1026 |
Лобов А.Г. |
14700 |
… |
|
Затем СУБД внесет изменения в таблицу tblStudent. tblStudent
StudentId |
StudentName |
Grant |
… |
1023 |
Бобров А.А. |
10500 |
… |
1025 |
Мухина В.Н. |
12600 |
… |
1026 |
Лобов А.Г. |
14700 |
… |
Таким образом, СУБД вызовет триггер 3 раза.
Если для таблицы tblStudent с обновленным содержанием пользователь попытается выполнить команду
Update tblStudent Set Grant = 12000
То триггер будет вызван также 3 раза, но условие, которое проверяет триггер, будет верным только для одной записи и триггер запишет правильное значение в запись с идентификатором 1023, в остальные записи триггер не внесет исправлений и СУБД, запишет в них указанное пользователем значение 12000. Таблица tblStudent будет содержать следующие записи:
tblStudent
|
StudentId |
|
StudentName |
|
Grant |
|
… |
|
|
|
|
|
|
||||
|
1023 |
|
Бобров А.А. |
|
11025 |
|
… |
|
|
1025 |
|
Мухина В.Н. |
|
12000 |
|
… |
|
|
1026 |
|
Лобов А.Г. |
|
12000 |
|
… |
|
Конспект лекций по дисциплине «Базы данных» (Процедуры и триггеры) |
15 |
ПетрГУ, кафедра прикладной математики и кибернетики
Пример 2
Ограничение предметной области: количество практик по дисциплине математического факультета должно превышать количество часов лекций.
Это условие будем проверять для операции добавления новых данных в таблицу tblSubject.
CREATE TRIGGER tgrNewSubjectInsert BEFORE INSERT
ON tblSubject BEGIN
DECLARE FName char(100);
SELECT FName= FacultyName FROM tblFaculty WHERE FacultyId=NEW.FacultyId;
IF (FName =’математический’) and (NEW.LecturesHour >= NEW.PracticeHour)
BEGIN
Print('На математическом факультете практик должно быть больше, чем лекций');
ROLLBACK TRANSACTION;
END END
До добавления новой записи в табицу tblSubject будет вызван триггер tgrNewSubjectInsert, который в таблице tblFaculty по идентификатору факультета узнает название факультета, и запишет его в переменную FName; далее триггер проверит условие, что факультет носит имя «математический» и количество часов лекций меньше или равно количеству часов практик, если условие будет верным, то триггер выведет сообщение об ошибке и выполнит откат транзакции, который приведет к отмене ввода новой записи.
Например, пусть таблица tblFaculty имеет следующие записи: tblFaculty
FacultyId |
FacultyName |
DeenName |
FacultyRoomId |
FacultyPhone |
FacultyStudent |
1 |
Физико- |
Звонов А.П. |
132 |
710945 |
568 |
|
технический |
|
|
|
|
|
|
|
|
|
|
2 |
Математический |
Кашин О.И. |
243 |
713498 |
603 |
|
|
|
|
|
|
Если пользователь попытается выполнить следующую команду:
Insert into tblSubject(SubjectName, LecturesHour, PracticeHour, FacultyId) values(‘Информатика’, 30, 51, 2), values(‘Информатика’, 30, 20, 1)
Эта команда будет успешно выполнена, так как первая добавляемая запись вносит дисциплину «Информатика» для математического факультета с количеством практик 51, а количеством лекций – 30, т.е. ограничение предметной области не нарушается, а вторая запись добавляет предмет «Информатика» для физико-технического факультета, и хотя здесь количество часов практик не превосходит количество часов лекций, но предмет будет
Конспект лекций по дисциплине «Базы данных» (Процедуры и триггеры) |
16 |
ПетрГУ, кафедра прикладной математики и кибернетики
читаться не на математическом факультете, поэтому ограничение предметной области здесь то же не нарушается.
Если пользователь попытается выполнить команду:
Insert into tblSubject(SubjectName, LecturesHour, PracticeHour, FacultyId) values(‘Алгебра’, 30, 51, 2), values(‘Физика’, 30, 20, 2)
Эта команда не будет выполнена. В таблицу tblSubject не будет добавлена ни одна запись, хотя первая добавляемая запись удовлетворяет ограничениям предметной области, но так как она добавляется в таблицу в рамках одной команды (одной транзакции) с другой записью, которая не удовлетворяет ограничениям предметной области, то обе записи не будут добавлены в таблицу (будет вызван откат транзакции, выполнявшей команду Insert).
Пример 3
Ограничение предметной области: количество студентов в одной группе не должно превышать значение 25.
Это условие будем проверять для операции добавления новых данных в таблицу tblStudent.
CREATE TRIGGER tgrNewStudentInsert AFTER INSERT ON tblStudent
BEGIN
DECLARE idGroup int, GCount int;
SELECT GCount=count(*) FROM tblStudent WHERE GroupId=NEW.GroupId; IF (GCount >25)
BEGIN
Print ('В группе не может быть более 25 студентов'); ROLLBACK TRANSACTION;
END END
После добавления новых записей будет вызван триггер, который подсчитает количество студентов, которые учатся в той же группе, в которую зачислен студент, и запишет его в переменную GCount; далее триггер проверит условие, что количество студентов в группе не превосходит значения 25, если условие будет верным, то триггер вызовет исключительную ситуацию и откат транзакции, которые приведут к отмене ввода всех записей.
Триггер вызывается после выполнения команды, чтобы правильно рассчитать количество студентов в группе с учетом нового студента.
Пример 4
Поддержка ограничения, связывающего данные двух таблиц.
Таблица tblFaculty включает столбец с именем FacultyStudent, который должен содержать суммарное количество студентов, обучающихся на факультете. Каждый студент описывается
Конспект лекций по дисциплине «Базы данных» (Процедуры и триггеры) |
17 |
ПетрГУ, кафедра прикладной математики и кибернетики
записью в таблице tblStudent. Следовательно, данные в таблицах tblFaculty и tblStudent должны быть согласованы, т.е., если в таблице tblFaculty указано, что на математическом факультете обучается 214 студентов, то в таблице tblStudent должно быть ровно 214 записей с информацией о 214-ти студентах математического факультета, не считая записи для других факультетов. И если в таблицу tblStudent добавляется новый студент, или удаляется информация о студенте, то соответственно должно измениться значение в таблице tblFaculty. Для автоматического согласования данных в двух таблицах создадим два триггера для таблицы tblStudent.
CREATE TRIGGER tgrStudentInsert AFTER INSERT, UPDATE
ON tblStudent BEGIN
DECLARE idFaculty int, TotalSum int;
Select idFaculty=FacultyId From tblGroup Where GroupId=NEW.GroupId; EXECUTE prStudentsOfFaculty (idFaculty, TotalSum);
UPDATE tblFaculty SET FacultyStudent=TotalSum WHERE FacultyId=idFaculty;
END
CREATE TRIGGER tgrStudentDelete AFTER DELETE ON tblStudent
BEGIN
OPEN DelCursor;
DECLARE idFaculty int, TotalSum int;
Select idFaculty=FacultyId From tblGroup Where GroupId=OLD.GroupId; EXECUTE prStudentsOfFaculty (idFaculty, TotalSum);
UPDATE tblFaculty SET FacultyStudent=TotalSum WHERE FacultyId=idFaculty;
END
Триггер tgrStudentInsert вызывается для операций Insert и Update, а триггер tgrStudentDelete – для операции Delete. Работают они одинаково, единственное отличие заключается в том, что при добавлении записей используется переменная NEW, а при удалении – OLD, поэтому триггеры отличаются только формулировкой запроса для получения идентификатора факультета.
Для каждой изменяемой (добавляемой/удаляемой) записи триггер по идентификатору группы находит идентификатор факультета, на котором учится студент, вызывает процедуру, которая подсчитывает общее количество студентов на факультете, и вносит правильное значение в соответствующую строку таблицы tblFaculty.
Конспект лекций по дисциплине «Базы данных» (Процедуры и триггеры) |
18 |
ПетрГУ, кафедра прикладной математики и кибернетики
Пример 5
Генерация уникального идентификатора аудитории.
При добавлении новых данных в таблицу tblRoom необходимо указать значение идентификатора записи. Так как эти значения абсолютно условны, т.е. их не существует в предметной области, они были введены в структуру таблицы в качестве искусственного ключа, то не стоит утруждать пользователя в выдумывании этих значений. Эти значения должны генерироваться автоматически и быть уникальными. Для генерации нового значения воспользуемся механизмом триггера и будем использовать простейший алгоритм, заключающийся в нахождении максимального идентификатора для уже имеющихся в таблице tblRoom записей, и увеличении этого значения на единицу, таким образом, получим новое уникальное значение.
CREATE TRIGGER tgrRoomInsert
BEFORE INSERT ON tblRoom
BEGIN
DECLARE idRoom int;
SELECT idRoom = max(RoomId) from tblRoom;
SET idRoom=idRoom+1;
NEW.RoomId=idRoom;
END
Пусть таблица tblRoom содержит следующие записи: tblRoom
RoomId |
RoomNumber |
PlaceCount |
SmartBoard |
RoomFloor |
|
|
|
|
|
1 |
105 |
27 |
1 |
1 |
|
|
|
|
|
3 |
201 |
14 |
0 |
2 |
|
|
|
|
|
7 |
203 |
30 |
0 |
2 |
|
|
|
|
|
Если пользователь попытается выполнить следующую команду
insert into tblRoom(RoomNumber, RoomFloor) values(‘327a’, 3), (‘403’, 4)
таблица tblRoom станет такой: tblRoom
RoomId |
RoomNumber |
PlaceCount |
SmartBoard |
RoomFloor |
|
|
|
|
|
1 |
105 |
27 |
1 |
1 |
|
|
|
|
|
3 |
201 |
14 |
0 |
2 |
|
|
|
|
|
7 |
203 |
30 |
0 |
2 |
|
|
|
|
|
8 |
327a |
Null |
Null |
3 |
|
|
|
|
|
9 |
403 |
Null |
Null |
4 |
|
|
|
|
|
Конспект лекций по дисциплине «Базы данных» (Процедуры и триггеры) |
19 |
ПетрГУ, кафедра прикладной математики и кибернетики
Для двух добавленных записей будут сгенерированы значения поля RoomId: 8 и 9 соответственно. Так как пользователь не указал значения для полей PlaceCount и SmartBoard, то эти поля получили значения NULL.
Конспект лекций по дисциплине «Базы данных» (Процедуры и триггеры) |
20 |