Бази даних-20210115T104840Z-001 / Реферат на тему _Современные СУБД_ / Using_MySql,_MS_SQL_Server_and_Oracle
.pdfПример 33: контроль операций модификации данных
MS SQL |
і |
Решение 4.2.1.a (триггер для таблицы subscriptions, второй вариант решения) (продолжение) |
| |
41IF ((LEN @bad records act future) > 0) OR
42(LEN @bad_records_deact_past) > 0 ) OR
43(LEN @bad records act greater than deact) > 0 )
44BEGIN
45SET @msg = 'Some records were NOT inserted!';
46IF (LEN @bad records act future 1 > 0
47BEGIN
48SET @msg = CONCAT @msg CHAR 13 , CHAR(10 ,
49'The following activation dates are in the future: ',
50@bad records act future ;
51END;
52IF (LEN @bad records deact_past) > 0
53BEGIN
54SET @msg = CONCAT @msg CHAR 13 , CHAR(10 ,
55'The following deactivation dates are in the past: ',
56@bad_records_deact_past ;
57END;
58IF (LEN @bad records act greater than deact > 0
59BEGIN
60SET @msg = CONCAT @msg CHAR 13 , CHAR(10 ,
61'The following deactivation dates are less than activation dates: ',
62@bad records act greater than deact ;
63END;
64RAISERROR @msg, 16, 1);
65END;
66 |
|
|
67 |
SELECT @good records = STUFF((SELECT ', ' + |
|
68 |
CAST([sb start] AS NVARCHAR) + '/' + |
|
69 |
CAST([sb finish] AS NVARCHAR) |
|
70 |
FROM [inserted] |
|
71 |
WHERE ( [sb start] <= CONVERT(date, GETDATE())) AND |
|
72 |
[sb finish] >= CONVERT(date, GETDATE())) AND |
|
73 |
[sb finish] >= [sb start])) |
|
74 |
ORDER BY [sb start] |
[sb finish] |
75 |
FOR XML PATH(''), TYPE).value '.', 'nvarchar(max)'), |
|
76 |
1 2. ''); |
|
77 |
|
|
78IF LEN @good records > 0
79BEGIN
80SET IDENTITY INSERT [subscriptions] ON;
81INSERT INTO [subscriptions]
82 |
[sb id], |
83 |
[sb subscriber], |
84 |
[sb book], |
85 |
[sb start], |
86 |
[sb finish], |
87 |
[sb is active]) |
88 |
SELECT ( CASE |
89 |
WHEN [sb id] IS NULL |
90 |
OR [sb id] = 0 THEN IDENT CURRENT('subscriptions') |
91 |
+ IDENT INCR('subscriptions') |
92 |
+ ROW NUMBER() OVER (ORDER BY |
93 |
(SELECT 1)) |
94 |
- 1 |
95 |
ELSE [sb id] |
96 |
END ) AS [sb id], |
97 |
[sb subscriber], |
98 |
[sb book], |
99 |
[sb start], |
100[sb finish],
101[sb is active]
102FROM [inserted]
103WHERE ( [sb start] <= CONVERT(date, GETDATE())) AND
104 |
([sb finish] >= CONVERT(date, GETDATE())) AND |
105 |
([sb finish] >= [sb start] ); |
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 340/545
Пример 33: контроль операций модификации данных
MS SQL |
Решение 4.2.1.a (триггер для таблицы subscriptions, второй вариант решения) (окончание) |
|
106 |
............. SET IDENTITY_INSERT |
.........................[Subscriptions] |
107 |
............. ........... |
OFF; |
108SET @msg =
109CONCAT('Subscriptions with the following activation/deactivation dates
110were inserted successfully: ', @good_records);
111PRINT @msg;
END; 112 GO
Код в строках 41 -65 проверяет, были ли обнаружены «плохие» записи и, если да, формирует сообщение об ошибке, учитывающее все три условия задачи. Поскольку после отправки сообщения об ошибке (строка 64) мы не откатываем транзакцию и не выходим из тела триггера, выполнение продолжается дальше, и мы получаем возможность произвести вставку в таблицу «хороших» записей.
В строках 67-76 формируется список таких «хороших» записей, данные в которых не нарушают ни одного из условий задачи. Если такие записи были обнаружены, в строках 78-111 мы выполняем их вставку в таблицу и выводим сообщение (просто сообщение, не сообщение об ошибке) со списком их дат. Легко догадаться, что эта операция вставки не приводит к повторной активации INSTEAD OF триггера (иначе мы получили бы бесконечную рекурсию).
Важно! Этот (второй) вариант решения показан в учебных целях для демонстрации возможностей триггеров MS SQL Server. В реальных приложениях такая «частичная» обработка данных (когда часть записей успешно вставляется в таблицу, а часть — нет) может привести к сложнообнаружимым дефектам и иным слабопредсказуемым последствиям.
Итак, оба варианты решений готовы, осталось проверить их работоспособность. Будем выполнять запросы и отслеживать полученные сообщения.
Выполним вставку данных с явно указанными значениями первичного ключа и частью записей, удовлетворяющей условиям задачи, а частью — не удовлетворяющей:
MS SQL I Решение 4.2.1.a (проверка работоспособности) |
1SET IDENTITY INSERT [subscriptions] ON;
2INSERT INTO [subscriptions]
3 |
|
([sb id] , |
4 |
|
[sb subscriber], |
5 |
|
[sb book] |
6 |
|
[sb start], |
7 |
|
[sb finish], |
8 |
|
[sb is active]) |
9 |
VALUES |
500, |
10 |
|
3 |
11 |
|
3 |
12 |
|
'2020-01-12', |
13 |
|
'2020-02-12', |
14 |
|
'N'), |
15 |
|
(600 |
16 |
|
3 |
17 |
|
4 |
18 |
|
'2021-01-12', |
19 |
|
'2021-02-12', |
20 |
|
'N'), |
21 |
|
(700 |
22 |
|
4 |
23 |
|
4 |
24 |
|
'2001-01-12', |
25 |
|
'2021-02-12', |
26 |
|
'N'); |
27 |
SET IDENTITY_ INSERT [subscriptions] OFF; |
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 341/545
Пример 33: контроль операций модификации данных
Полученные сообщения:
• В первом варианте решения — сообщение об ошибке:
The following subscriptions' activation dates are in the future: 500 (2020- 01-12), 600 (2021-01-12)
• Во втором варианте решения: о Сообщение об ошибке:
Some records were NOT inserted! The following activation dates are in
the future: 2020-01-12, |
2021-01-12 |
о Информационное сообщение:
Subscriptions with the following activation/deactivation dates were inserted successfully: 2001-01-12/2021-02-12
Выполним вставку данных с без указания значений первичного ключа и с частью записей, удовлетворяющей условиям задачи, а частью — не удовлетворяющей:
MS SQL |
Решение 4.2.1 .a (проверка работоспособности) | |
||
1 |
INSERT INTO [subscriptions] |
||
2 |
|
|
([sb subscriber] |
3 |
|
|
[sb book], |
4 |
|
|
[sb start], |
5 |
|
|
[sb finish] |
6 |
|
|
[sb is active] |
7 |
VALUES |
(3, |
|
8 |
|
|
3, |
9 |
|
|
'2020-01-12', |
10 |
|
|
'2020-02-12', |
11 |
|
|
'N'), |
12 |
|
|
(3, |
13 |
|
|
4, |
14 |
|
|
'2021-01-12', |
15 |
|
|
'2021-02-12', |
16 |
|
|
'N'), |
17 |
|
|
(4, |
18 |
|
|
4, |
19 |
|
|
'2001-01-12', |
20 |
|
|
'2021-02-12', |
21 |
|
|
'N' ) |
|
|
|
|
Полученные сообщения:
•В первом варианте решения — сообщение об ошибке:
The following subscriptions' deactivation dates are in the past: 704 (2001- 01-12), 705 (2002-01-12)
•Во втором варианте решения:
оСообщение об ошибке:
Some records were NOT inserted! The following deactivation dates are
in the past: 2001-02-12, |
2002-02-12 |
о Информационное сообщение:
Subscriptions with the following activation/deactivation dates were inserted successfully: 2001-01-12/2021-02-12
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 342/545
Пример 33: контроль операций модификации данных
Выполним вставку данных, удовлетворяющих всем условиям задачи:
MS SQL I Решение 4.2.1.a (проверка работоспособности) |
1 |
INSERT INTO [subscriptions] |
||
2 |
|
([sb subscriber], |
|
3 |
|
[sb |
book] |
4 |
|
[sb |
start], |
5 |
|
[sb |
finish], |
6 |
|
[sb |
is active]) |
7 |
VALUES |
4 , |
|
8 |
|
4 , |
|
9 |
|
'2001-01-12', |
|
10 |
|
'2021-02-12', |
|
11 |
|
'N' |
) |
|
|
|
|
Полученные сообщения:
•В первом варианте решения: никаких сообщений от триггера нет.
•Во втором варианте решения:
оСообщение об ошибке: отсутствует.
оИнформационное сообщение:
Subscriptions with the following activation/deactivation dates were inserted successfully: 2001-01-12/2021-02-12
Выполним обновление данных с нарушением одного из условия задачи:
MS SQL Решение 4.2.1 .а (проверка работоспособности)
1 |
UPDATE [subscriptions] |
|
2 |
SET |
[sb_finish] = '2005-01-01' |
3 |
WHERE |
[sb start] > '2011-01-01' |
Триггер во втором варианте решения не реагирует на операцию обновления, а от триггера в первом варианте решения поступит следующее сообщение об ошибке:
The following subscriptions' deactivation dates are less than activation dates: 2
(act: 2011-01-12, deact: 2005-01-01), |
3 (act: 2012-05-17, deact: 2005-01-01), |
|
42 (act: 2012-06-11, deact: 2005-01-01), |
57 (act: 2012-06-11, deact: 2005-0101), |
|
|
61 |
(act: 2014-08-03, deact: 2005-01-01), |
|
62 |
(act: 2014-08-03, deact: 200501-01), |
|
86 |
(act: 2014-08-03, deact: 2005-01-01), |
|
91 |
(act: 2015-10-07, deact: |
2005-01-01), 95 (act: 2015-10-07, deact: |
2005-01-01), 99 (act: 2015-10-08, deact: |
|
2005-01-01), 100 (act: 2011-01-12, deact: 2005-01-01) |
Выполним обновление данных с соблюдением всех условий задачи:
MS SQL і Решение 4.2.1 .а (проверка работоспособности)
1 |
UPDATE |
[subscriptions] |
2 |
SET |
[sb_finish] = '2002-01-01' |
3 |
WHERE |
[sb start] = '2001-01-12'; |
Триггер во втором варианте решения не реагирует на операцию обновления, а от триггера в первом варианте решения не поступит никаких сообщений.
Итак, решение данной задачи для MS SQL Server получено и проверено. Переходим к решению для Oracle.
Поскольку Oracle не поддерживает псевдотаблицы deleted и inserted, мы реализуем ту же логику, что и в решении для MySQL, используя триггеры уровня записи.
Таким образом, отличие в решении для Oracle от решения для MySQL будет только в способе отмена операции (с одновременным выводом сообщения об ошибке): в Oracle для таких задач удобно использовать функцию RAISE_APPLICA-
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 343/545
Пример 33: контроль операций модификации данных
TION_ERROR.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 344/545
Пример 33: контроль операций модификации данных
В остальном решения для Oracle и MySQL полностью идентичны.
Oracl |
і Решение 4.2.1.a (триггеры для таблицы subscriptions) |
| |
|
|
||
e |
|
|
||||
|
|
|
|
|
|
|
1 |
-- Реакция на добавление выдачи книги. |
|
|
|
||
2 |
CREATE TRIGGER "subscriptions control ins" |
|
|
|
||
3 |
AFTER INSERT |
|
|
|
|
|
4 |
ON "subscriptions" |
|
|
|
|
|
5 |
FOR EACH ROW |
|
|
|
|
|
6 |
BEGIN |
|
|
|
|
|
7 |
|
|
|
|
|
|
8 |
-- Блокировка выдач книг с датой выдачи в будущем. |
|
||||
9 |
IF |
new "sb start" > TRUNC(SYSDATE) |
|
|
|
|
10 |
THEN |
|
|
|
|
|
11 |
RAISE APPLICATION ERROR( 20001 |
'Date ' || |
new "sb start" || |
|||
12 |
|
' for subscription ' || |
new "sb id" || |
|||
13 |
|
' activation is in the future.'); |
||||
14 |
END IF; |
|
|
|
|
|
15 |
|
|
|
|
|
|
16-- Блокировка выдач книг с датой возврата в прошлом.
17IF new "sb finish" < TRUNC(SYSDATE)
18THEN
19 |
RAISE APPLICATION ERROR( 20002 'Date ' || new "sb finish" || |
20 |
' for subscription ' || new "sb id" || |
21 |
' deactivation is in the past.'); |
22 |
END IF; |
23 |
|
24-- Блокировка выдач книг с датой возврата меньшей, чем дата выдачи.
25IF new "sb finish" < new "sb start"
26THEN
27 |
RAISE APPLICATION ERROR( 20003 'Date ' || |
new "sb finish" || |
|
28 |
' |
for subscription ' || new "sb id" || |
|
29 |
' |
deactivation is less than the date |
|
30 |
|
for its activation (' || |
|
31 |
|
new "sb start" || |
').'); |
32END IF;
33END;
34
35-- Реакция на обновление выдачи книги.
36CREATE TRIGGER "subscriptions_control_upd"
37AFTER UPDATE
38ON "subscriptions"
39FOR EACH ROW
40BEGIN
41
42-- Блокировка выдач книг с датой выдачи в будущем.
43IF new "sb start" > TRUNC(SYSDATE)
44THEN
45 |
RAISE APPLICATION ERROR( 20001 'Date ' || |
new "sb start" || |
46 |
' for subscription |
' || new "sb id" || |
47 |
' activation is in |
the future.'); |
48 |
END IF; |
|
49 |
|
|
50-- Блокировка выдач книг с датой возврата меньшей, чем дата выдачи.
51IF new "sb finish" < new "sb start"
52THEN
53 |
RAISE APPLICATION ERROR( 20003 'Date ' || new "sb finish" || |
54 |
' for subscription ' || new "sb id" || |
55 |
' deactivation is less than the date |
56 |
for its activation (' || |
57 |
new "sb start" || ').'); |
58 |
END IF; |
59 |
|
60 |
END; |
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 345/545
Пример 33: контроль операций модификации данных
Проверить работоспособность полученного решения можно с помощью следующих запросов (их логика и ожидаемая реакция триггеров рассмотрены в реше-
нии для MySQL).
Oracle |
Решение 4.2.1.a (проверка работоспособности) |
1--
2Деактивация триггера, формирующего значение автоинкрементируемого ПК:
3 |
ALTER TRIGGER |
"TRG_subscriptions_sb_id" DISABLE; |
4 |
|
|
5-- Добавление выдачи книги с датой активации в будущем:
6INSERT
7VALUES
8 |
|
|
9 |
INTO "subscriptions" 500, 1 ■ 1 , TO_DATE('2020-01-12', 'YYYY-MM- |
|
10 |
DD'), |
|
11 |
TO_DATE('2020-02-12', 'YYYY-MM-DD'), |
|
12 |
'N'); |
|
13 |
|
|
14 |
-- |
Активация триггера, |
15 |
формирующего значение |
автоинкрементируемого ПК: |
16 |
ALTER TRIGGER |
"TRG_subscriptions_sb_id" ENABLE; |
17 |
|
|
18 |
-- |
Добавление выдачи книги с датой |
19 |
активации |
в будущем |
20-- (без указания значения первичного ключа):
21INSERT
22 |
|
|
23 |
|
|
24 |
|
|
25 |
|
INTO "subscriptions" "sb_subscriber", "sb_book" "sb_start" |
26 |
|
"sb_finish", "sb_is_active" |
27 |
VALUES |
3 , |
28 |
|
3 , |
29 |
|
TO_DATE('2020-01-12', 'YYYY-MM-DD'), |
30 |
|
TO_DATE('2020-02-12', 'YYYY-MM-DD'), |
31 |
|
'N'); |
32 |
|
|
33-- Добавление выдачи книги с датой возврата в прошлом:
34INSERT
35 |
|
|
|
36 |
|
|
|
37 |
|
|
|
38 |
|
INTO "subscriptions" "sb_subscriber", "sb_book" "sb_start" |
|
39 |
|
"sb_finish", "sb_is_active" |
|
40 |
VALUES |
1, |
|
41 |
|
1 ■ |
|
42 |
|
TO_DATE('2000-01-12', 'YYYY-MM-DD'), |
|
43 |
|
TO_DATE('2000-02-12', |
'YYYY-MM-DD'), |
|
|
|
|
44 |
|
'N'); |
|
|
|
|
|
45 |
|
|
|
46 |
-- Добавление выдачи книги без нарушения условий задачи: |
||
47 |
INSERT |
|
|
48 |
|
|
|
49 |
|
|
|
50 |
|
|
|
51 |
|
INTO "subscriptions" "sb_subscriber", "sb_book" "sb_start" |
|
|
|
|
|
52 |
|
"sb_finish", "sb_is_active" |
|
|
|
|
|
53 |
VALUES |
1, |
|
54 |
|
1 ■ |
|
55 |
|
TO_DATE('2000-01-12', |
'YYYY-MM-DD'), |
|
|
|
|
56 |
|
TO_DATE('2020-02-12', |
'YYYY-MM-DD'), |
57 |
'N'); |
|
|
58 |
|
59--Обновление добавленной выдачи книги таким образом, чтобы дата
60--её активации оказалась в будущем:
UPDATE "subscriptions"
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 346/545
Пример 33: контроль операций модификации данных
SET "sb_start" = TO_DATE('2020-01-01', 'YYYY-MM-DD')
WHERE "sb id" = 104;
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 347/545
Пример 33: контроль операций модификации данных
Oracle I |
Решение 4.2.1 .a (проверка работоспособности) (продолжение) |
| |
61-- Обновление добавленной выдачи книги таким образом, чтобы
62-- дата её активации оказалась позже даты возврата:
63UPDATE "subscriptions"
64 |
SET |
"sb start" = TO DATE('2010-01-01', 'YYYY-MM-DD'), |
|||
65 |
|
"sb |
finish" = |
TO DATE('2005-01- |
'YYYY-MM-DD' ) |
66 |
WHERE |
"sb |
id" = 104 |
|
|
67 |
|
|
|
|
|
68-- Обновление добавленной выдачи книги таким образом, чтобы
69-- дата её возврата была в прошлом (для операции обновления
70-- такое разрешено):
71UPDATE "subscriptions"
72 |
SET |
"sb start" = TO DATE('2005-01-01', 'YYYY-MM-DD'), |
|
73 |
|
"sb finish" = TO DATE('2006-01- |
'YYYY-MM-DD' ) |
74 |
WHERE |
"sb id" = 104; |
|
75 |
|
|
|
76 |
-- Обновление добавленной выдачи книги без нарушения условий задачи: |
||
77 |
UPDATE |
"subscriptions" |
|
78 |
SET |
"sb start" = TO DATE('2005-01-01', 'YYYY-MM-DD'), |
|
79 |
|
"sb finish" = TO DATE('2010-01- |
'YYYY-MM-DD') |
80 |
WHERE |
"sb id" = 104; |
|
На этом решение данной задачи завершено.
■ЛУ Решение 4.2.1.b{315}.
На примере этой (достаточно простой) задачи продемонстрируем типичное неправильное решение, которое часто первым приходит в голову. Оно состоит в том, чтобы в AFTER-триггере проверить, существуют ли читатели, для которых нарушается условие задачи (выборкой по всем читателям) и, если да, «откатить транзакцию». На достаточно объёмной базе данных такое решение может приводить к очень заметному падению производительности.
Правильное же решение состоит в том, чтобы в BEFORE-триггере произво-
дить проверку выполнения условия задачи только для того читателя (тех читателей
— в MS SQL Server), для которого сейчас выполняется операция вставки или обновления записи в таблице subscriptions.
Итак, для всех трёх СУБД представим неправильное и правильное решение и сравним скорость их работы на базе данных «Большая библиотека».
Внеправильном решении для MySQL создадим INSERT- и UPDATE-триггеры
сполностью идентичным кодом, в котором будем формировать список читателей, для которых было нарушено условие задачи (недопустимость выдачи более десяти книг).
При крайней неоптимальности с точки зрения производительности у этого решения всё же есть один плюс: оно будет реагировать в том числе и на все нарушения условия задачи, которые были совершены до создания триггера. Однако, если такое поведение нас не устраивает, этот плюс превращается в минус, и представленное решение становится ещё хуже, чем мы думали.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 348/545
Пример 33: контроль операций модификации данных
|
MySQL 1 |
Решение 4.2.1.b (неправильное решение) |
| |
1 |
DELIMITER $$ |
|
|
2 |
|
|
|
3 |
CREATE TRIGGER 'sbs cntrl 10 books ins WRONG' |
||
4 |
AFTER INSERT |
|
|
5 |
ON 'subscriptions' |
|
|
6 |
FOR EACH ROW |
|
|
7 |
|
BEGIN |
|
8 |
|
|
|
9 |
|
SET @msg = IFNULL((SELECT GROUP CONCAT( |
10 |
CONCAT('(id=', 's id', ', ', 's name', |
||
11 |
', books=', 's books', ')') SEPARATOR ', ') |
||
12 |
AS 'list' |
||
13 |
FROM |
(SELECT 's id', |
|
14 |
|
|
's name', |
15 |
|
|
COUNT('sb book') AS 's books' |
16 |
FROM |
'subscribers' |
|
17 |
|
|
JOIN 'subscriptions' |
18 |
|
|
ON 's id' = 'sb subscriber' |
19 |
WHERE |
'sb is active' = 'Y' |
|
20 |
GROUP |
BY 'sb subscriber' |
|
21 |
HAVING 's books' > 10) AS 'prepared data'), |
||
22 |
|
|
''); |
23 |
|
|
|
24IF (LENGTH @msg > 0)
25THEN
26SET @msg = CONCAT('The following readers have more books
27 |
than allowed (10 allowed): ', @msg ; |
28SIGNAL SQLSTATE '45001' SET MESSAGE TEXT = @msg, MYSQL ERRNO = 1001;
29END IF;
30
31END;
32$$
33
34CREATE TRIGGER 'sbs_cntrl_10_books_upd_WRONG'
35AFTER UPDATE
36ON 'subscriptions'
37FOR EACH ROW
38BEGIN
39 |
|
|
|
40 |
SET @msg = IFNULL((SELECT GROUP CONCAT( |
||
41 |
CONCAT('(id=', 's id', ', ', 's name', |
||
42 |
', books=', 's books', ')') SEPARATOR ', ') |
||
43 |
AS 'list' |
||
|
|
|
|
44 |
FROM |
(SELECT 's id', |
|
45 |
|
|
's name', |
46 |
|
|
COUNT('sb book') AS 's books' |
47 |
FROM |
'subscribers' |
|
48 |
|
|
JOIN 'subscriptions' |
49 |
|
|
ON 's id' = 'sb subscriber' |
50 |
WHERE |
'sb is active' = 'Y' |
|
51 |
GROUP |
BY 'sb subscriber' |
|
52 |
HAVING 's books' > 10) AS 'prepared data'), |
||
53 |
|
|
''); |
54 |
|
|
|
55IF (LENGTH @msg > 0)
56THEN
57SET @msg = CONCAT('The following readers have more books
58 |
than allowed (10 allowed): ', @msg ; |
59SIGNAL SQLSTATE '45001' SET MESSAGE TEXT = @msg, MYSQL ERRNO = 1001;
60END IF;
61
62END;
63$$
64
65 DELIMITER ;
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 349/545