Пример 35: прозрачное исправление ошибок в данных
MS SQL I |
Решение 4.2.3.b (триггеры для таблицы subscriptions) (продолжение) |
| |
24SET IDENTITY INSERT [subscriptions] ON;
25INSERT INTO [subscriptions]
26 |
|
([sb id], |
27 |
|
[sb subscriber], |
28 |
|
[sb book] |
29 |
|
[sb start], |
30 |
|
[sb finish], |
31 |
|
[sb is active]) |
32 |
SELECT ( CASE |
|
33 |
|
WHEN [sb id] IS NULL |
34 |
|
OR [sb id] = 0 THEN IDENT CURRENT('subscriptions') |
35 |
|
+ IDENT INCR('subscriptions') |
36 |
|
+ ROW NUMBER() OVER (ORDER BY |
37 |
|
(SELECT 1 ) |
38 |
|
- 1 |
39 |
|
ELSE [sb id] |
40 |
|
END ) AS [sb id] |
41 |
|
[sb subscriber], |
42 |
|
[sb book], |
43 |
|
[sb start] |
44 |
|
( CASE |
45 |
|
WHEN (([sb finish] < [sb start]) OR |
46 |
|
([sb finish] < GETDATE())) |
47 |
|
THEN DATEADD(month, 2 GETDATE()) |
48 |
|
ELSE [sb finish] |
49 |
|
END ) AS [sb finish] |
50 |
|
[sb is active] |
51 |
FROM |
[inserted] |
52SET IDENTITY INSERT [subscriptions] OFF;
53GO
54
55-- Внимание! Чтобы этот триггер можно было создать, необходимо
56-- отключить каскадное обновление на внешних
57-- ключах таблицы [subscriptions].
58-- Правда, тогда придётся доработать триггер так, чтобы с его
59-- помощью обеспечивать ссылочную целостность.
60CREATE TRIGGER [sbscs date tm upd]
61ON [subscriptions]
62INSTEAD OF UPDATE
63AS
64DECLARE @bad records NVARCHAR(max);
65DECLARE @msg NVARCHAR(max);
66
67IF (UPDATE([sb id]))
68BEGIN
69RAISERROR ('Please, do NOT update surrogate PK
70 |
on table [subscriptions]!', 16 1); |
71ROLLBACK TRANSACTION;
72RETURN;
73END;
74 |
|
|
75 |
SELECT @bad records = |
|
76 |
STUFF((SELECT ', ' + '[' + CAST([sb finish] AS NVARCHAR) + |
|
77 |
|
'] -> [' + FORMAT(DATEADD(month, 2, GETDATE()), |
78 |
|
'yyyy-MM-dd') + ']' |
79 |
FROM |
[inserted] |
80 |
WHERE |
([sb finish] < [sb start]) OR |
81 |
|
([sb finish] < GETDATE()) |
82FOR XML PATH(''), TYPE) value ('.', 'nvarchar(max) '),
831, 2, '');
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 370/545
Пример 35: прозрачное исправление ошибок в данных
MS SQL I |
Решение 4.2.3.b (триггеры для таблицы subscriptions) (продолжение) |
| |
|||||
84 |
|
IF (LEN'@bad records |
> 0) |
|
|
||
85 |
|
|
BEGIN |
|
|
|
|
86 |
|
|
SET @msg = CONCAT('Some values were changed: ', @bad records ; |
||||
87 |
|
|
PRINT @msg |
|
|
|
|
88 |
|
|
RAISERROR (@msg 16, 0 ; |
|
|
||
89 |
|
|
END; |
|
|
|
|
90 |
|
|
|
|
|
|
|
91 |
|
UPDATE [subscriptions] |
|
|
|||
92 |
|
SET |
[subscriptions] [sb subscriber] = [inserted] [sb subscriber], |
||||
93 |
|
|
|
[subscriptions] [sb book] = [inserted] [sb book], |
|||
94 |
|
|
|
[subscriptions] [sb start] = [inserted] [sb start], |
|||
95 |
|
|
|
[subscriptions] [sb finish] = |
|
|
|
96 |
|
|
|
( CASE |
|
|
|
97 |
|
|
|
WHEN ( [inserted] [sb finish] < [inserted] [sb start]1 OR |
|||
98 |
|
|
|
|
[inserted] [sb finish] < GETDATE())) |
||
99 |
|
|
|
THEN DATEADD(month, 2, GETDATE()) |
|
||
100 |
|
|
|
ELSE [inserted] [sb finish] |
|
|
|
101 |
|
|
|
END ), |
|
|
|
102 |
|
|
|
[subscriptions] [sb is active] = [inserted] [sb is active] |
|||
103 |
|
FROM |
[subscriptions] |
|
|
||
104 |
|
|
|
JOIN [inserted] |
|
|
|
105 |
|
|
|
ON [subscriptions] [sb id] = [inserted] [sb id]; |
|||
106 |
GO |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Oracl |
|
і |
Решение 4.2.3.b (триггеры для таблицы subscriptions) |
| |
|
||
|
|
|
|||||
e |
|
|
|
|
|
|
|
1 |
|
CREATE TRIGGER "sbscs date tm ins upd" |
|
|
|||
2 |
|
BEFORE INSERT OR UPDATE |
|
|
|
||
3 |
|
ON "subscriptions" |
|
|
|
||
4 |
|
FOR EACH ROW |
|
|
|
||
5 |
|
DECLARE |
|
|
|
|
|
6 |
|
|
new value DATE; |
|
|
|
|
7 |
|
|
BEGIN |
|
|
|
|
8 |
|
|
IF (:new "sb finish" < :new "sb start" |
OR ( new "sb finish" < SYSDATE) |
|||
9 |
|
|
THEN |
|
|
|
|
10 |
|
|
new value := ADD MONTHS 2 SYSDATE); |
|
|
||
11 |
|
|
DBMS OUTPUT.PUT LINE('Value [' || new "sb finish" || |
||||
12 |
|
|
'] was automatically changed to [' || |
|
|
||
13 |
|
|
TO CHAR new value |
'YYYY-MM-DD') || ']'); |
|
||
14 |
|
|
new "sb finish" := new value |
|
|
||
15 |
|
|
END IF; |
|
|
|
|
16 |
|
|
END; |
|
|
|
|
|
|
|
|
|
|
|
|
На этом решение данной задачи завершено. Убедиться в его корректности вы можете самостоятельно, выполнив запросы к таблице subscriptions на вставку и обновление данных — как нарушающие условие задачи, так и не нарушающие.
Задание 4.2.3.TSK.A: доработать решение{347} задачи 4.2.3.b{344} таким образом, чтобы исходные и автоматически полученные скорректированные значения даты в сообщениях, выводимых триггерами, всегда гарантированно представляли дату в одинаковом формате (в текущей реализации формат исходного и полученного значения может различаться).
& Задание 4.2.3.TSK.B: доработать решение{347} задачи 4.2.3.b{344} для MS SQL Server таким образом, чтобы получить возможность создания INSTEAD OF
UPDATE триггера и в то же время не потерять каскадное обновление внешних ключей таблицы subscriptions (иными словами: отключить каскадное обновление на самих внешних ключах и реализовать его «вручную» в INSTEAD
OF UPDATE триггере).
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 371/545
Пример 35: прозрачное исправление ошибок в данных
Задание 4.2.3.TSK.C: создать триггер, корректирующий название книги таким образом, чтобы оно удовлетворяло следующим условиям:
•не допускается наличие пробелов в начале и конце названия;
•не допускается наличие повторяющихся пробелов;
•первая буква в названии всегда должна быть заглавной.
Задание 4.2.3.TSK.D: создать триггер, меняющий дату выдачи книги на текущую, если указанная в SQL-запросе дата выдачи книги меньше текущей на полгода и более.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 372/545
Пример 36: выборка и модификация данных с использованием хранимых функций
Раздел 5: Использование хранимых функций и процедур
5.1. Использование хранимых функций
5.1.1.Пример 36: выборка и модификация данных с использованием хранимых функций
ОЗадача 5.1.1.a{353}: создать хранимую функцию, получающую на вход даты выдачи и возврата книги и возвращающую разницу между этими датами в
днях, а также слова « [OK]», « [NOTICE]», « [WARNING]», соответственно, если разница в днях составляет менее десяти, от десяти до тридцати и более тридцати дней.
О Задача 5.1.1.b{355}: создать хранимую функцию, возвращающую список свободных значений автоинкрементируемых первичных ключей в указанной
таблице (свободными считаются значения первичного ключа, которые отсутствуют в таблице, и при этом меньше максимального используемого значения; например, если в таблице есть первичные ключи 1, 3, 8, то свободными считаются 2, 4, 5, 6, 7).
О Задача 5.1.1.c{367}: создать хранимую функцию, актуализирующую данные в таблице books_statistics (см. задачу 3.1.2.a{215}) и возвращающую число,
показывающее изменение количества фактически имеющихся в библиотеке книг.
Ожидаемый результат 5.1.1 .a.
Результат выполнения запроса, извлекающего идентификатор, даты выдачи и возврата и результат работы функции для случаев, когда книга не возвращена, должен выглядеть так:
sb_id |
sb_start |
sb_finish |
rdns |
3 |
2012-05-17 |
2012-07-17 |
61 WARNING |
62 |
2014-08-03 |
2014-10-03 |
61 WARNING |
86 |
2014-08-03 |
2014-09-03 |
31 WARNING |
91 |
2015-10-07 |
2015-03-07 |
-214 OK |
99 |
2015-10-08 |
2025-11-08 |
3684 WARNING |
Ї, |
Ожидаемый результат 5.1.1.b. |
||
Допускается три формата представления результата:
•таблица из двух колонок, в которых хранятся значения начала и конца диапазонов «свободных ключей»;
•таблица из одной колонки с перечислением всех имеющихся значений «свободных ключей»;
•строка с перечислением всех имеющихся значений «свободных ключей».
Ожидаемый результат 5.1.1.c.
При вызове функции данные в таблице books_statistics актуализируются, функция возвращает разницу между предыдущим и новым значением количества фактически имеющихся в библиотеке книг.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 373/545
Пример 36: выборка и модификация данных с использованием хранимых функций
Чу? Решение 5.1.1.a{352}.
Хранимые процедуры и функции в общем случае могут быть крайне сложными и неочевидными, а их синтаксическим особенностям в документации к каждой СУБД посвящены десятки страниц. Потому в рассматриваемых задачах осознанно сказано создать такие хранимые процедуры, которые можно с минимальными отличиями реализовать во всех трёх СУБД.
Одну «теоретическую особенность» всё же упомянем: во всех трёх представленных ниже решениях данной задачи функции объявлены как детерминированные (в MS SQL Server этот эффект достигается с помощью конструкции WITH
SCHEMABINDING). Это означает, что каждый раз при вызове с одинаковыми пара-
метрами на одинаковых наборах данных (для текущей задачи это не актуально, но если бы мы обращались к данным в таблицах БД, это было бы важно) такие функции будут возвращать одинаковые значения. Указание такого свойства хранимой функции позволяет СУБД более эффективно использовать механизмы внутренней оптимизации и повысить производительность.
В остальном логика решения проста: мы получаем на вход две даты, вычисляем их разницу и сохраняем результат в переменную, на основе значения этой переменной определяем текстовую часть сообщения, затем возвращаем результат конкатенации этой переменной и полученного сообщения.
И это — всё, дальше — только сам код. Решение для MySQL:
MySQL Решение 5.1.1
1DELIMITER.а $$
2CREATE FUNCTION READ DURATION AND STATUS start date DATE, finish date DATE)
3RETURNS VARCHAR(150 DETERMINISTIC
4BEGIN
5DECLARE days INT;
6DECLARE message VARCHAR 150);
7SET days = DATEDIFF finish date, start date);
8CASE
9WHEN (days 10 THEN SET message = ' OK';
10WHEN ( days>=10| AND days<=30)) THEN SET message = ' NOTICE';
11WHEN (days 30 THEN SET message = ' WARNING';
12END CASE;
13RETURN CONCAT(days message);
14END$$
15
16 DELIMITER ;
Для проверки корректности полученного решения нужно использовать следующий запрос:
MySQL і Решение 5.1.1.а (проверка работоспособности)
1 |
SELECT 'sb |
id', |
|
2 |
|
'sb |
start', |
3 |
|
'sb |
finish', |
4 |
|
READ DURATION AND STATUS('sb start', 'sb finish') AS 'rdns' |
|
5 |
FROM |
'subscriptions' |
|
6 |
WHERE |
'sb |
is active' = 'Y' |
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 374/545