Материал: Using_MySql,_MS_SQL_Server_and_Oracle(1)

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Пример 34: контроль формата и значений данных

MySQL Решение 4.2.2.a (триггеры для таблицы subscribers) (продолжение)

20CREATE TRIGGER `sbsrs_cntrl_name_upd`

21BEFORE UPDATE

22ON `subscribers`

23FOR EACH ROW

24BEGIN

25IF ((CAST(NEW.`s_name` AS CHAR CHARACTER SET cp1251) REGEXP

26CAST('^[a-zA-Zа-яА-ЯёЁ\'-]+([^a-zA-Zа-яА-ЯёЁ\'-]+[a-zA-Zа-яА-

27ЯёЁ\'.-]+){1,}$' AS CHAR CHARACTER SET cp1251)) = 0)

28OR (LOCATE('.', NEW.`s_name`) = 0)

29THEN

30SET @msg = CONCAT('Subscribers name should contain at

31

 

least two words and one point, but the following

32

 

name violates this rule: ', NEW.`s_name`);

33SIGNAL SQLSTATE '45001' SET MESSAGE_TEXT = @msg, MYSQL_ERRNO = 1001;

34END IF;

35END;

36$$

37

38 DELIMITER ;

Поскольку MS SQL Server не поддерживает полноценные регулярные выражения, здесь мы используем альтернативное решение на основе подсчёта оставшихся в строке пробелов.

Вторая проблема MS SQL Server и его триггеров уровня выражения состоит в том, что в UPDATE-триггере мы обязаны запретить изменение первичного ключа (строки 50-56), иначе мы не сможем гарантированно корректно выполнить в коде триггера операцию обновления данных.

Третья уже знакомая нам проблема MS SQL Server связана с необходимостью вычисления значения автоинкрементируемого первичного ключа (строки 2938) в INSERT-триггере (см. пояснение в решении{246} задачи 3.2.1.a{245}).

Стоит отметить, что если объём данных у нас небольшой и производительность не снижается сколь бы то ни было заметным образом от использования AF- TER-триггеров, то решение этой задачи можно сделать гораздо более коротким, простым и универсальным (код INSERT- и UPDATE-триггера будет полностью идентичным). Убедитесь в этом самостоятельно, выполнив задание 4.2.2.TSK.B{343}.

MS SQL Решение 4.2.2.a (триггеры для таблицы subscribers)

1CREATE TRIGGER [sbsrs_cntrl_name_ins]

2ON [subscribers]

3INSTEAD OF INSERT

4AS

5DECLARE @bad_records NVARCHAR(max);

6DECLARE @msg NVARCHAR(max);

7

 

 

 

8

 

SELECT @bad_records = STUFF((SELECT

', ' + [s_name]

9

 

FROM

[inserted]

10

 

WHERE

 

11

 

CHARINDEX(' ', LTRIM(RTRIM([s_name]))) = 0

 

 

 

12

 

OR CHARINDEX('.', [s_name]) = 0

13

 

FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'),

14

 

1, 2, '');

 

15

 

 

 

16IF (LEN(@bad_records) > 0)

17BEGIN

18SET @msg = CONCAT('Subscribers name should contain at least two

19

 

words and one point, but the following names

20

 

violate this rule: ', @bad_records);

21RAISERROR (@msg, 16, 1);

22ROLLBACK TRANSACTION;

23RETURN;

24END;

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 340/545

Пример 34: контроль формата и значений данных

MS SQL Решение 4.2.2.a (триггеры для таблицы subscribers) (продолжение)

25SET IDENTITY_INSERT [subscribers] ON;

26INSERT INTO [subscribers]

27

 

([s_id],

28

 

[s_name])

29

 

SELECT ( CASE

30

 

WHEN [s_id] IS NULL

31

 

OR [s_id] = 0 THEN IDENT_CURRENT('subscribers')

32

 

+ IDENT_INCR('subscribers')

33

 

+ ROW_NUMBER() OVER (ORDER BY

34

 

(SELECT 1))

35

 

- 1

36

 

ELSE [s_id]

37

 

END ) AS [s_id],

38

 

[s_name]

39

 

FROM [inserted];

40SET IDENTITY_INSERT [subscribers] OFF;

41GO

42

43CREATE TRIGGER [sbsrs_cntrl_name_upd]

44ON [subscribers]

45INSTEAD OF UPDATE

46AS

47DECLARE @bad_records NVARCHAR(max);

48DECLARE @msg NVARCHAR(max);

49

50IF (UPDATE([s_id]))

51BEGIN

52RAISERROR ('Please, do NOT update surrogate PK

53

on table [subscribers]!', 16, 1);

54ROLLBACK TRANSACTION;

55RETURN;

56END;

57

 

 

 

58

 

SELECT @bad_records = STUFF((SELECT

', ' + [s_name]

59

 

FROM

[inserted]

60

 

WHERE

 

61

 

CHARINDEX(' ', LTRIM(RTRIM([s_name]))) = 0

62

 

OR CHARINDEX('.', [s_name]) = 0

63

 

FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'),

64

 

1, 2, '');

 

65

 

 

 

66IF (LEN(@bad_records) > 0)

67BEGIN

68SET @msg = CONCAT('Subscribers name should contain at least two

69

 

words and one point, but the following names

70

 

violate this rule: ', @bad_records);

71RAISERROR (@msg, 16, 1);

72ROLLBACK TRANSACTION;

73RETURN;

74END;

75

 

 

 

76

 

UPDATE

[subscribers]

77

 

SET

[subscribers].[s_name] = [inserted].[s_name]

78

 

FROM

[subscribers]

79JOIN [inserted]

80ON [subscribers].[s_id] = [inserted].[s_id];

81GO

Переходим к решению для Oracle, которое полностью повторяет логику решения для MySQL — BEFORE-триггер на основе регулярного выражения и функции проверки существования подстроки в строке.

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 341/545

Пример 34: контроль формата и значений данных

Oracle

Решение 4.2.2.a (триггеры для таблицы subscribers)

1CREATE TRIGGER "sbsrs_cntrl_name_ins_upd"

2BEFORE INSERT OR UPDATE

3ON "subscribers"

4FOR EACH ROW

5BEGIN

6IF ((NOT REGEXP_LIKE(:new."s_name", '^[a-zA-Zа-яА-ЯёЁ''-]+([^a-zA-Zа-яА-

7ЯёЁ''-]+[a-zA-Zа-яА-ЯёЁ''.-]+){1,}$'))

8OR (INSTRC(:new."s_name", '.', 1, 1) = 0))

9THEN

10RAISE_APPLICATION_ERROR(-20001, 'Subscribers name should contain

11

 

at least two

words and

one point,

12

 

but the following name

violates

13

 

this rule: '

|| :new."s_name");

14END IF;

15END;

16

17

18

На этом решение данной задачи завершено. Убедиться в его корректности вы можете самостоятельно, выполнив запросы к таблице subscribers на вставку и обновление данных — как нарушающие условие задачи, так и не нарушающие.

Решение 4.2.2.b{338}.

Поскольку условие данной задачи во многом схоже с предыдущей, реализуем самое простое решение (для MS SQL Server используем AFTER-триггер) и ограничимся лишь кодом без подробных пояснений:

MySQL

Решение 4.2.2.b (триггеры для таблицы books)

1 DELIMITER $$

2

3CREATE TRIGGER `books_cntrl_year_ins`

4BEFORE INSERT

5ON `books`

6FOR EACH ROW

7BEGIN

8IF ((YEAR(CURDATE()) - NEW.`b_year`) > 100)

9THEN

10SET @msg = CONCAT('The following issuing year is more than

11

 

100 years in the past: ', NEW.`b_year`);

12SIGNAL SQLSTATE '45001' SET MESSAGE_TEXT = @msg, MYSQL_ERRNO = 1001;

13END IF;

14END;

15$$

16

17CREATE TRIGGER `books_cntrl_year_upd`

18BEFORE UPDATE

19ON `books`

20FOR EACH ROW

21BEGIN

22IF ((YEAR(CURDATE()) - NEW.`b_year`) > 100)

23THEN

24SET @msg = CONCAT('The following issuing year is more than

25

 

100 years in the past: ', NEW.`b_year`);

26SIGNAL SQLSTATE '45001' SET MESSAGE_TEXT = @msg, MYSQL_ERRNO = 1001;

27END IF;

28END;

29$$

30

31 DELIMITER ;

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 342/545

Пример 34: контроль формата и значений данных

MS SQL

Решение 4.2.2.b (триггеры для таблицы books)

1CREATE TRIGGER [books_cntrl_year_ins_upd]

2ON [books]

3AFTER INSERT, UPDATE

4AS

5DECLARE @bad_records NVARCHAR(max);

6DECLARE @msg NVARCHAR(max);

7

 

 

 

8

 

SELECT @bad_records = STUFF((SELECT

', ' + CAST([b_year] AS NVARCHAR)

9

 

FROM

[inserted]

10

 

WHERE

(YEAR(GETDATE()) - [b_year]) > 100

11

 

FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'),

12

 

1, 2, '');

 

13

 

 

 

14IF (LEN(@bad_records) > 0)

15BEGIN

16SET @msg = CONCAT('The following issuing years are more

17

 

than 100 years in the past: ', @bad_records);

18RAISERROR (@msg, 16, 1);

19ROLLBACK TRANSACTION;

20RETURN;

21END;

22GO

Oracle

Решение 4.2.2.b (триггеры для таблицы books)

1CREATE TRIGGER "books_cntrl_year_ins_upd"

2BEFORE INSERT OR UPDATE

3ON "books"

4FOR EACH ROW

5BEGIN

6IF ((TO_NUMBER(TO_CHAR(SYSDATE, 'YYYY')) - :new."b_year") > 100)

7THEN

8RAISE_APPLICATION_ERROR(-20001, 'The following issuing year is

9

 

more than 100 years in the past: '

10

 

|| :new."b_year");

11END IF;

12END;

На этом решение данной задачи завершено. Убедиться в его корректности вы можете самостоятельно, выполнив запросы к таблице books на вставку и обновление данных — как нарушающие условие задачи, так и не нарушающие.

Задание 4.2.2.TSK.A: модифицировать решение{338} задачи 4.2.2.a{338} для MySQL и Oracle так, чтобы в коде триггеров не использовались регулярные выражения.

Задание 4.2.2.TSK.B: переписать решение{338} задачи 4.2.2.a{338} для MS SQL Server с использованием AFTER-триггеров.

Задание 4.2.2.TSK.C: переписать регулярные выражения в решении{338} задачи 4.2.2.a{338} для MySQL и Oracle так, чтобы:

исключить необходимость отдельной проверки наличия точки в имени читателя;

допустить нахождение точки в любом из слов (а не только во втором и далее, как это сделано сейчас).

Задание 4.2.2.TSK.D: создать триггер, допускающий регистрацию в библиотеке только таких автором, имя которых не содержит никаких символов кроме букв, цифр, знаков - (минус), ' (апостроф) и пробелов (не допускается два и более идущих подряд пробела).

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 343/545

Пример 35: прозрачное исправление ошибок в данных

4.2.3. Пример 35: прозрачное исправление ошибок в данных

Задача 4.2.3.a{344}: создать триггер, проверяющий наличие точки в конце имени читателя и добавляющий таковую при её отсутствии.

Задача 4.2.3.b{347}: создать триггер, меняющий дату возврата книги на «два месяца с момента выдачи», если дата возврата меньше даты выдачи или находится в прошлом.

Ожидаемый результат 4.2.3.a.

При выполнении операции модификации данных, нарушающей условие задачи, данные должны быть откорректированы в соответствии с условием задачи. Также должно быть выведено информационное сообщение в стиле: «Value [Иванов И.И] was automatically changed to [Иванов И.И.]».

Ожидаемый результат 4.2.3.b.

При выполнении операции модификации данных, нарушающей условие задачи, данные должны быть откорректированы в соответствии с условием задачи. Также должно быть выведено информационное сообщение в стиле: «Return date 2020.01.01 is less than giveaway date 2021.01.01. Return date changed to 2020.03.01.»

Решение 4.2.3.a{344}.

Приведённый ниже код триггеров для MySQL отличается от множества ранее рассмотренных подобных примеров только значением SQLSTATE: значения, начинающиеся с 01, сообщают не об ошибках, а о предупреждениях. И это — единственный способ сообщить из MySQL-триггера о выполненном преобразовании некорректного значения поля в корректное.

К сожалению, до версии 5.7.216 MySQL хранит предупреждения не по всей текущей сессии, а только по последнему выражению, потому в нашем случае (с использованием MySQL 5.6) мы не увидим этих сообщений. Но сами триггеры при этом корректно выполняют свою работу и корректируют некорректные данные.

MySQL Решение 4.2.3.a (триггеры для таблицы subscribers)

1 DELIMITER $$

2

3CREATE TRIGGER `sbsrs_name_lp_ins`

4BEFORE INSERT

5ON `subscribers`

6FOR EACH ROW

7BEGIN

8IF (SUBSTRING(NEW.`s_name`, -1) <> '.')

9THEN

10SET @new_value = CONCAT(NEW.`s_name`, '.');

11SET @msg = CONCAT('Value [', NEW.`s_name`, '] was automatically

12

 

changed to [', @new_value ,']');

13SET NEW.`s_name` = @new_value;

14SIGNAL SQLSTATE '01000' SET MESSAGE_TEXT = @msg, MYSQL_ERRNO = 1000;

15END IF;

16END;

17$$

16 http://dev.mysql.com/doc/refman/5.7/en/show-warnings.html

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 344/545

Источник: https://studfile.net/preview/16418462/