Материал: Using_MySql,_MS_SQL_Server_and_Oracle

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

Пример 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) мы не увидим этих сообщений. Но сами триггеры при этом корректно выполняют свою работу и корректируют некорректные данные.

Решение 4.2.3.a

для

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/showwarnings.html

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

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

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

18CREATE TRIGGER 'sbsrs_name_lp_upd'

19BEFORE UPDATE

20ON 'subscribers'

21FOR EACH ROW

22BEGIN

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

24THEN

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

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

27

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

28SET NEW.'s_name' = @new_value;

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

30END IF;

31END;

32$$

33

34DELIMITER ;

ВMS SQL Server так же просто и красиво подменить некорректноезначение корректным не получится, т.к. в этой СУБД нет триггеров уровня записи.

Потому нам придётся создавать INSTEAD OF триггеры (со всемивытекающими отсюда проблемами и ограничениями в виде «ручной» генерациизначения автоинкрементируемого первичного ключа на вставке данных и запрета изменения значения первичного ключа на обновлении данных).

Зато в MS SQL Server можно очень легко и удобно передавать сообщения из триггеров. Для демонстрации этих возможностей в представленном ниже коде реализовано два варианта поведения:

В строках 18 и 68: с помощью конструкции PRINT (которая просто выводит текстовое сообщение в консоль).

В строках 19 и 69: с помощью функции RAISERROR (которая при таких параметрах (см. документацию16) генерирует сообщение, не приводящее к остановке операции; к тому же мы не «откатываем» транзакцию в теле триггера).

MS SQL

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

1

CREATE TRIGGER [sbsrs name lp ins]

 

2

ON [subscribers]

 

 

3

INSTEAD OF INSERT

 

 

4

AS

 

 

5

 

DECLARE @bad records NVARCHAR(max);

6

 

DECLARE @msg NVARCHAR(max);

 

7

 

 

 

 

8

 

SELECT @bad records = STUFF((SELECT ', ' + '[' + [s name] + '] -> [' +

9

 

 

 

[s name] + '.]'

10

 

 

FROM

[inserted]

11

 

 

WHERE

RIGHT([s name], 1 <> '.'

12

 

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

13

 

1, 2

'');

 

14

 

 

 

 

15

 

IF (LEN @bad records) > 0

 

16

 

BEGIN

 

 

17

 

SET @msg = CONCAT('Some values were changed: ', @bad records);

18

 

PRINT @msg;

 

 

19

 

RAISERROR @msg

16 0);

 

20

 

END;

 

 

 

 

 

 

 

16 https://msdn.microsoft.com/en-us/library/ms178592.aspx

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

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

MS SQL I

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

|

21SET IDENTITY INSERT [subscribers] ON;

22INSERT INTO [subscribers]

23

 

([s id],

 

24

 

[s name])

 

25

SELECT ( CASE

 

26

 

WHEN [s id] IS NULL

 

27

 

OR [s id] = 0 THEN IDENT CURRENT('subscribers')

28

 

+ IDENT INCR('subscribers')

29

 

+ ROW NUMBER() OVER (ORDER BY

30

 

 

(SELECT 1 )

31

 

 

- 1

32

 

ELSE [s id]

33

 

END ) AS [s id]

 

34

 

( CASE

 

35

 

WHEN RIGHT([s name], 1

<> '.'

36

 

THEN CONCAT [s name]

'.')

37

 

ELSE [s name]

 

38

 

END ) AS [s name]

 

39

FROM

[inserted]

 

40SET IDENTITY INSERT [subscribers] OFF;

41GO

42

43CREATE TRIGGER [sbsrs name lp 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

 

[s name] + '.]'

 

60

FROM

[inserted]

 

61

WHERE

RIGHT([s name],

1 <> '.'

62

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

63

1, 2 '');

 

 

64

 

 

 

65IF (LEN @bad records) > 0

66BEGIN

67SET @msg = CONCAT('Some values were changed: ', @bad records);

68PRINT @msg;

69

RAISERROR @msg 16 0);

70

END;

 

71

 

 

72

UPDATE [subscribers]

73

SET

[subscribers] [s name] =

74

 

( CASE

75

 

WHEN RIGHT([inserted] [s name], 1 <> '.'

76

 

THEN CONCAT [inserted] [s name], '.')

77

 

ELSE [inserted] [s name]

78

 

END )

79

FROM

[subscribers]

80

 

JOIN [inserted]

81

 

ON [subscribers] [s id] = [inserted] [s id]

82

GO

 

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

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

Решение для Oracle традиционно повторяет логику решения для MySQL с той лишь разницей, что здесь мы можем только вывести текстовое сообщение (сгенерировать предупреждение, не влияющее на выполнение операции, на текущий момент в Oracle нельзя).

Чтобы увидеть выводимое сообщение необходимо предварительно выполнить команду SET SERVEROUTPUT ON, включающую показ таких данных.

 

Oracl

і

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

|

e

 

 

 

 

 

 

 

1

 

CREATE TRIGGER "sbsrs name lp ins upd"

 

2

 

BEFORE INSERT OR UPDATE

 

 

 

3

 

ON "subscribers"

 

 

 

4

 

FOR EACH ROW

 

 

 

5

 

DECLARE

 

 

 

6

 

 

new value NVARCHAR2 150);

 

 

 

7

 

 

BEGIN

 

 

 

8

 

 

IF (SUBSTR(:new "s name"

-1

<> '.')

 

9THEN

10new value := CONCAT( new "s name" '.');

11DBMS OUTPUT.PUT LINE('Value [' || new "s name" ||

12'] was automatically changed to [' || new value || ']');

13new "s name" := new value

14END IF;

15END;

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

иобновление данных — как нарушающие условие задачи, так и не нарушающие.

уЦ7

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

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

в MySQL и MS SQL Server приходится делать два отдельных триггера (MySQL не умеет «объединять» объявление триггеров для нескольких операций, а в MS SQL Server различается внутреннее поведение INSERT- и UPDATE-

триггера), в то время как в Oracle получается компактный одинаковый код, актуальный для обеих операций;

в MS SQL Server без доработки модели БД можно создать только INSTEAD OF INSERT триггер, а для создания INSTEAD OF UPDATE триггера придётся отключить каскадное обновление на внешних ключах таблицы subscriptions и реализовать соответствующие операции по обеспечению ссылочной целостности в самом триггере;

отображаемые триггерами сообщения об автоматической корректировке значения поля sb_finish в представленной ниже реализации могут содержать начальное и конечное значение даты в разных форматах (чтобы этого избежать, нужно явно приводить оба значения к одинаковому формату даты).

Востальном все представленные далее триггеры содержат лишь вариации на тему рассмотренных ранее операций.

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

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

MySQL I

Решение 4.2.3.b (триггеры для таблицы subscriptions) |

 

1

DELIMITER $$

 

2

 

 

 

3

CREATE TRIGGER 'sbscs date tm ins'

 

4

BEFORE INSERT

 

5

ON 'subscriptions'

 

6

 

FOR EACH ROW

 

7

 

BEGIN

 

8

 

IF (NEW.'sb finish' < NEW.'sb start') OR (NEW.'sb

< CURDATE() )

9

 

THEN

 

10

 

SET @new value = DATE ADD(CURDATE(), INTERVAL 2

 

11

 

SET @msg = CONCAT('Value [', NEW.'sb finish', '] was automatically

12

 

changed to [', @new value ,']');

 

13

 

SET NEW.'sb finish' = @new value;

 

14

 

SIGNAL SQLSTATE '01000' SET MESSAGE TEXT = @msg, MYSQL ERRNO = 1000;

15

 

END IF;

 

16

 

END;

 

17

$$

 

18

 

 

 

19

CREATE TRIGGER 'sbscs date tm upd'

 

20

BEFORE UPDATE

 

21

ON 'subscriptions'

 

22

 

FOR EACH ROW

 

23

 

BEGIN

 

24

 

IF (NEW.'sb finish' < NEW.'sb start') OR (NEW.'sb

< CURDATE() )

25

 

THEN

 

26

 

SET @new value = DATE ADD(CURDATE(), INTERVAL 2

 

27

 

SET @msg = CONCAT('Value [', NEW.'sb finish', '] was automatically

28

 

changed to [', @new value ,']');

 

29

 

SET NEW.'sb finish' = @new value;

 

30

 

SIGNAL SQLSTATE '01000' SET MESSAGE TEXT = @msg, MYSQL ERRNO = 1000;

31

 

END IF;

 

32

 

END;

 

33

$$

 

34

 

 

 

35

DELIMITER ;

 

 

 

 

 

MS SQL Решение 4.2.3.b (триггеры для таблицы subscriptions)

1

CREATE TRIGGER [sbscs date tm ins]

2

ON [subscriptions]

 

3

INSTEAD OF INSERT

 

4

AS

 

 

5

DECLARE @bad records NVARCHAR(max);

6

DECLARE @msg NVARCHAR(max);

7

 

 

 

8

SELECT @bad records =

 

9

STUFF((SELECT ', ' + '[' + CAST([sb finish] AS NVARCHAR) +

10

 

'] -> [' + FORMAT(DATEADD(month, 2, GETDATE()),

11

 

 

'yyyy-MM-dd') + ']'

12

 

FROM

[inserted]

13

 

WHERE

([sb finish] < [sb start]) OR

14

 

 

([sb finish] < GETDATE())

15

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

16

1, 2,

'');

 

17

 

 

 

18

IF (LEN @bad records) > 0

19

BEGIN

 

 

20

SET @msg = CONCAT('Some values were changed: ', @bad records);

21

PRINT @msg;

 

 

22

RAISERROR

@msg 16

0);

23

END;

 

 

 

 

 

 

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

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