Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
11
Добавлен:
15.01.2021
Размер:
10.35 Mб
Скачать

Пример 41: управление неявными транзакциями

MS SQL Решение 6.1.1.a

1-- Автоподтверждение выключено:

2SET IMPLICIT_TRANSACTIONS ON;

3

 

 

 

4

 

SELECT

COUNT(*)

5

 

FROM

[subscribers]; -- 4

6

 

 

 

7

 

INSERT

INTO [subscribers]

8

 

 

([s_name])

9

 

VALUES

(N'Иванов И.И.');

10

 

 

 

11

 

SELECT

COUNT(*)

12

 

FROM

[subscribers]; -- 5

13

 

 

 

14

 

ROLLBACK;

15

 

 

 

16

 

SELECT

COUNT(*)

17

 

FROM

[subscribers]; -- 4

18

 

 

 

19-- Автоподтверждение включено:

20SET IMPLICIT_TRANSACTIONS OFF;

21

 

 

 

22

 

SELECT

COUNT(*)

23

 

FROM

[subscribers]; -- 4

24

 

 

 

25

 

INSERT

INTO [subscribers]

26

 

 

([s_name])

27

 

VALUES

(N'Иванов И.И.');

28

 

 

 

29

 

SELECT

COUNT(*)

30

 

FROM

[subscribers]; -- 5

31

 

 

 

32ROLLBACK; -- Ошибка! Нет соответствующей транзакции, которую

33-- можно было бы отменить.

34

 

 

 

35

 

SELECT

COUNT(*)

36

 

FROM

[subscribers]; -- 5

Встроках 1-17 запросы выполняются в режиме отключённого автоподтверждения неявных транзакций: именно поэтому отмена транзакции в строке 14 проходит успешно и вставка данных, выполненная в строках 7-9, аннулируется.

Встроках 19-36 запросы выполняются в режиме включённого автоподтверждения неявных транзакций, и потому отмена транзакции в строке 32 ни на что не влияет: вставка данных, выполненная в строках 25-27, остаётся в силе.

В MS SQL Server существует одна важная особенность, которую необходимо учитывать. Если в режиме IMPLICIT_TRANSACTIONS ON использовать выражение BEGIN TRANSACTION, СУБД читает созданную транзакцию вложенной (@@TRANCOUNT принимает значение 2) и для успешного подтверждения её выполнения необходимо использовать выражение COMMIT TRANSACTION дважды. В противном случае вы рискуете или получить «подвисшую» транзакцию (которая так и не завершена), или потерять результаты модификации данных (если закроете соединение с СУБД). При этом ROLLBACK TRANSACTION работает в обоих режимах одинаково, отменяя все транзакции вне зависимости от глубины их вложенности.

Эта проблема усугубляется тем, что при отладке запросов в средствах наподобие MS SQL Server Management Studio вы, как правило, работаете в рамках одного и того же соединения, и вместо «подвисшей» транзакции получаете продол-

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

Пример 41: управление неявными транзакциями

жение предыдущей (не закрытой ранее). Потому в большинстве случаев при отладке всё работает правильно, а в реальных приложениях поведение становится неверным.

Продемонстрируем только что описанное поведение MS SQL Server.

MS SQL Решение 6.1.1.a (демонстрация особенности работы MS SQL Server)

1-- Режим по умолчанию

2SET IMPLICIT_TRANSACTIONS OFF;

3PRINT @@TRANCOUNT; -- 0

4-- Старт первой ("родительской") транзакции

5BEGIN TRANSACTION;

6PRINT @@TRANCOUNT; -- 1

7-- Старт второй ("дочерней") транзакции

8BEGIN TRANSACTION;

9PRINT @@TRANCOUNT; -- 2

10-- Подтверждение второй ("дочерней") транзакции

11COMMIT TRANSACTION;

12PRINT @@TRANCOUNT; -- 1

13-- Подтверждение первой ("родительской") транзакции

14COMMIT TRANSACTION;

15PRINT @@TRANCOUNT; -- 0

16

17-- Режим "неявных транзакций"

18SET IMPLICIT_TRANSACTIONS ON;

19PRINT @@TRANCOUNT; -- 0

20-- Старт первой ("родительской") транзакции

21BEGIN TRANSACTION;

22PRINT @@TRANCOUNT; -- 2

23-- Старт второй ("дочерней") транзакции

24BEGIN TRANSACTION;

25PRINT @@TRANCOUNT; -- 3

26-- Подтверждение второй ("дочерней") транзакции

27COMMIT TRANSACTION;

28PRINT @@TRANCOUNT; -- 2

29-- Подтверждение первой ("родительской") транзакции

30COMMIT TRANSACTION;

31PRINT @@TRANCOUNT; -- 1

32-- Необходим ещё и этот COMMIT

33COMMIT TRANSACTION;

34PRINT @@TRANCOUNT; -- 0

35

36-- Режим по умолчанию

37SET IMPLICIT_TRANSACTIONS OFF;

38-- Старт первой ("родительской") транзакции

39BEGIN TRANSACTION;

40PRINT @@TRANCOUNT; -- 1

41-- Старт второй ("дочерней") транзакции

42BEGIN TRANSACTION;

43PRINT @@TRANCOUNT; -- 2

44-- Отмена всех транзакций

45ROLLBACK TRANSACTION;

46PRINT @@TRANCOUNT; -- 0

47

48-- Режим "неявных транзакций"

49SET IMPLICIT_TRANSACTIONS ON;

50-- Старт первой ("родительской") транзакции

51BEGIN TRANSACTION;

52PRINT @@TRANCOUNT; -- 2

53-- Старт второй ("дочерней") транзакции

54BEGIN TRANSACTION;

55PRINT @@TRANCOUNT; -- 3

56-- Отмена всех транзакций

57ROLLBACK TRANSACTION;

58PRINT @@TRANCOUNT; -- 0

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

Пример 41: управление неявными транзакциями

Oracle (в отличие от MySQL и MS SQL Server) не оперирует такими понятиями, как «неявная транзакция» и её автоподтверждение. Эта СУБД лишь автоматически подтверждает текущую транзакцию в случае, если выполняется выражение, модифицирующее структуру базы данных.

Однако клиентское ПО, организующее взаимодействие с Oracle, может иметь свои собственные настройки, отвечающие за автоматическое подтверждение транзакций, не обрамлённых явно выражениями по запуску и подтверждению или отмене.

В таком средстве как Oracle SQL Developer, например, соответствующий эффект достигается выполнением команды SET AUTOCOMMIT ON / OFF (эффект которой эквивалентен изменению параметра autocommit в MySQL).

Для решения данной задачи в Oracle необходимо использовать следующий набор запросов.

Oracle Решение 6.1.1.a

1-- Автоподтверждение выключено:

2SET AUTOCOMMIT OFF;

3

 

 

 

4

 

SELECT

COUNT(*)

5

 

FROM

"subscribers"; -- 4

6

 

 

 

7

 

INSERT

INTO "subscribers"

8

 

 

("s_name")

9

 

VALUES

(N'Иванов И.И.');

10

 

 

 

11

 

SELECT

COUNT(*)

12

 

FROM

"subscribers"; -- 5

13

 

 

 

14

 

ROLLBACK;

15

 

 

 

16

 

SELECT

COUNT(*)

17

 

FROM

"subscribers"; -- 4

18

 

 

 

 

 

 

 

19-- Автоподтверждение включено:

20SET AUTOCOMMIT ON;

21

 

 

 

22

 

SELECT

COUNT(*)

23

 

FROM

"subscribers"; -- 4

24

 

 

 

25

 

INSERT

INTO "subscribers"

26

 

 

("s_name")

27

 

VALUES

(N'Иванов И.И.');

28

 

 

 

29

 

SELECT

COUNT(*)

30

 

FROM

"subscribers"; -- 5

31

 

 

 

32

 

ROLLBACK;

33

 

 

 

34

 

SELECT

COUNT(*)

35

 

FROM

"subscribers"; -- 5

 

 

 

 

Встроках 1-17 запросы выполняются в режиме отключённого автоподтверждения неявных транзакций: именно поэтому отмена транзакции в строке 14 проходит успешно и вставка данных, выполненная в строках 7-9, аннулируется.

Встроках 19-35 запросы выполняются в режиме включённого автоподтверждения неявных транзакций, и потому отмена транзакции в строке 32 ни на что не влияет: вставка данных, выполненная в строках 25-27, остаётся в силе.

На этом решение данной задачи завершено.

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

Пример 41: управление неявными транзакциями

Решение 6.1.1.b{408}.

Данная задача призвана не только напомнить принципы работы с хранимыми процедурами и логику управления автоподтверждением неявных транзакций, она также демонстрирует разницу в производительности СУБД в ситуациях, когда при выполнении множества операций модификации данных каждая из них вступает в силу по-отдельности, и когда такие операции фиксируются по факту выполнения всей их группы целиком.

Традиционно мы начинаем решение с MySQL и сразу рассмотрим код.

MySQL

Решение 6.1.1.b (код процедуры)

1DELIMITER $$

2CREATE PROCEDURE TEST_INSERT_SPEED(IN records_count INT,

3

 

IN use_autocommit INT,

4

 

OUT total_time TIME(6))

5BEGIN

6DECLARE counter INT DEFAULT 0;

7

8SET @old_autocommit = (SELECT @@autocommit);

9SELECT CONCAT('Old autocommit value = ', @old_autocommit);

10SELECT CONCAT('New autocommit value = ', use_autocommit);

11

12IF (use_autocommit != @old_autocommit)

13THEN

14SELECT CONCAT('Switching autocommit to ', use_autocommit);

15SET autocommit = use_autocommit;

16ELSE

17SELECT 'No changes in autocommit mode needed.';

18END IF;

19

20SELECT CONCAT('Starting insert of ', records_count, ' records...');

21SET @start_time = (SELECT NOW(6));

22WHILE counter < records_count DO

23

 

INSERT INTO

`subscribers`

24

 

 

(`s_name`)

25VALUES (CONCAT('New subscriber ', (counter + 1)));

26SET counter = counter + 1;

27END WHILE;

28SET @finish_time = (SELECT NOW(6));

29SELECT CONCAT('Finished insert of ', records_count, ' records...');

30

31IF ((SELECT @@autocommit) = 0)

32THEN

33SELECT 'Current autocommit mode is 0. Performing explicit commit.';

34COMMIT;

35END IF;

36

37IF (use_autocommit != @old_autocommit)

38THEN

39SELECT CONCAT('Switching autocommit back to ', @old_autocommit);

40SET autocommit = @old_autocommit;

41ELSE

42SELECT 'No changes in autocommit mode were made. No restore needed.';

43END IF;

44

45SET total_time = (SELECT TIMEDIFF(@finish_time, @start_time));

46SELECT CONCAT('Time used: ', total_time);

47

48SELECT total_time;

49END;

50$$

51DELIMITER ;

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

Пример 41: управление неявными транзакциями

В строке 8 происходит определение текущего значения автоподтверждения неявных транзакций (в MySQL эту информацию можно извлечь из переменной

@@autocommit).

Встроках 12-18 происходит проверка необходимости изменения режима автоподтверждения неявных транзакций и само изменение (если это необходимо). В строках 37-34 происходит повторная проверка и возврат исходного значения, если оно было изменено.

Определение затраченного на выполнение операции вставки времени происходит за счёт получения текущего времени до (строка 21) и после (строка 28) выполнения цикла вставки (строки 22-27), а затем вычисления разности этих значений (строка 45).

Встроках 31-35 проверяется текущее значение режима автоподтверждения неявных транзакций и подтверждение выполняется явным образом в строке 34, если автоподтверждение выключено (здесь нас не интересует, было ли оно выключено изначально или в процессе выполнения нашей процедуры).

Теперь остаётся только вернуть значение затраченного на выполнение цикла вставки времени как результат работы хранимой процедуры (строка 48).

Для проверки работоспособности и оценки производительности MySQL в двух режимах работы с неявными транзакциями можно использовать следующие запросы.

MySQL Решение 6.1.1.b (код для проверки работоспособности)

1CALL TEST_INSERT_SPEED(100000, 1, @tmp);

2SELECT @tmp;

3

4CALL TEST_INSERT_SPEED(100000, 0, @tmp);

5SELECT @tmp;

Вы можете самостоятельно произвести соответствующее исследование производительности. Здесь лишь отметим, что отключение автоподтверждения неявных транзакций может ускорить данную операцию вставки в десятки раз.

На этом решение для MySQL завершено.

Переходим к MS SQL Server. Внутренняя логика хранимой процедуры будет очень похожа на решение для MySQL, и главным отличием будет лишь способ32 определения режима автоподтверждения неявных транзакций. Т.к. данная СУБД не предоставляет эту информацию явным образом, мы попытаемся определить её косвенно.

Такое определение основано на информации об уровне вложенности текущей транзакции (@@TRANCOUNT) и настройках текущего соединения (@@OPTIONS). В строках 12-31 кода хранимой процедуры мы рассматриваем все возможные интересующие нас сочетания значений этих параметров, выводим отладочную информацию и определяем, включён ли режим подтверждения неявных транзакций.

Встроках 36-45 мы определяем необходимость изменения режима автоподтверждения и меняем его, если это требуется.

Встроках 47-57 совершенно аналогично с решением для MySQL выполняется цикл вставки указанного количества записей.

Встроках 59-64 проверяется, в каком режиме запущена хранимая процедура (в случае с MySQL мы ориентировались на текущее значение переменной @@autocommit, но т.к. в MS SQL Server её нет, а определение текущего режима

довольно нетривиально (см. строки 11-31), мы полагаем, что работа идёт в том режиме, который указан при вызове хранимой процедуры).

32 http://stackoverflow.com/questions/2919018/in-sql-server-how-do-i-know-what-transaction-mode-im-currently-using

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

Пример 41: управление неявными транзакциями

Остаётся только восстановить исходное значение IMPLICIT_TRANSACTIONS (строки 65-74) и определить, сколько времени заняло выполнение цикла вставки записей (строки 76-80). Такая громоздкая конструкция с использованием функций CONVERT, DATEADD, DATEDIFF необходима для того, чтобы получить на выходе затраченное время в удобной для человека форме.

Итак, вот код процедуры.

 

MS SQL

Решение 6.1.1.b (код процедуры)

 

1

 

CREATE PROCEDURE TEST_INSERT_SPEED @records_count INT,

 

2

 

 

@use_autocommit INT,

 

3

 

 

@total_time TIME OUTPUT

4AS

5BEGIN

6DECLARE @counter INT = 0;

7DECLARE @old_autocommit INT = 0;

8DECLARE @start_time TIME;

9DECLARE @finish_time TIME;

10

11IF (@@TRANCOUNT = 0 AND (@@OPTIONS & 2 = 0))

12BEGIN

13PRINT 'IMPLICIT_TRANSACTIONS = OFF, no transaction is running.';

14SET @old_autocommit = 1;

15END

16ELSE IF (@@TRANCOUNT = 0 AND (@@OPTIONS & 2 = 2))

17BEGIN

18PRINT 'IMPLICIT_TRANSACTIONS = ON, no transaction is running.';

19SET @old_autocommit = 0;

20END

21ELSE IF (@@OPTIONS & 2 = 0)

22BEGIN

23PRINT 'IMPLICIT_TRANSACTIONS = OFF, explicit transaction is running.';

24SET @old_autocommit = 1;

25END

26ELSE

27BEGIN

28PRINT 'IMPLICIT_TRANSACTIONS = ON, implicit or explicit transaction

29is running.';

30SET @old_autocommit = 0;

31END;

32

33PRINT CONCAT('Old autocommit value = ', @old_autocommit);

34PRINT CONCAT('New autocommit value = ', @use_autocommit);

35

36IF (@use_autocommit != @old_autocommit)

37BEGIN

38PRINT CONCAT('Switching autocommit to ', @use_autocommit);

39IF (@use_autocommit = 1)

40SET IMPLICIT_TRANSACTIONS OFF;

41ELSE

42SET IMPLICIT_TRANSACTIONS ON;

43END

44ELSE

45PRINT 'No changes in autocommit mode needed.';

46

47PRINT CONCAT('Starting insert of ', @records_count, ' records...');

48SET @start_time = GETDATE();

49WHILE (@counter < @records_count)

50BEGIN

51INSERT INTO [subscribers]

52

 

([s_name])

53VALUES (CONCAT('New subscriber ', (@counter + 1)));

54SET @counter = @counter + 1;

55END;

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

Пример 41: управление неявными транзакциями

MS SQL

Решение 6.1.1.b (код процедуры) (продолжение)

56SET @finish_time = GETDATE();

57PRINT CONCAT('Finished insert of ', @records_count, ' records...');

58

59IF (@use_autocommit = 0)

60BEGIN

61PRINT 'Current autocommit mode is 0 (IMPLICIT_TRANSACTIONS = ON).

62Performing explicit commit.';

63COMMIT;

64END;

65IF (@use_autocommit != @old_autocommit)

66BEGIN

67PRINT CONCAT('Switching autocommit back to ', @old_autocommit);

68IF (@old_autocommit = 1)

69SET IMPLICIT_TRANSACTIONS OFF;

70ELSE

71SET IMPLICIT_TRANSACTIONS ON;

72END

73ELSE

74PRINT 'No changes in autocommit mode needed.';

75

 

 

76

 

SET @total_time = CONVERT(VARCHAR(12),

77

 

DATEADD(ms,

78

 

DATEDIFF(ms, @start_time, @finish_time),

79

 

0),

80

 

114);

81PRINT CONCAT('Time used: ', @total_time);

82RETURN;

83END;

84GO

Для проверки работоспособности и оценки производительности MS SQL Server в двух режимах работы с неявными транзакциями можно использовать следующие запросы.

MS SQL Решение 6.1.1.b (код для проверки работоспособности)

1DECLARE @t TIME;

2SET IMPLICIT_TRANSACTIONS ON

3EXECUTE TEST_INSERT_SPEED 10, 1, @t OUTPUT;

4PRINT CONCAT ('Stored procedure has returned the following value: ', @t);

5

6DECLARE @t TIME;

7SET IMPLICIT_TRANSACTIONS ON

8EXECUTE TEST_INSERT_SPEED 10, 0, @t OUTPUT;

9PRINT CONCAT ('Stored procedure has returned the following value: ', @t);

На этом решение для MySQL завершено.

Переходим к Oracle. Поскольку данная СУБД вообще не оперирует таким понятием как «автоподтверждение неявных транзакций», мы можем работать лишь в одном из двух режимов (который явно выбираем и реализуем сами):

выполнение подтверждения транзакции после каждой операции (аналог включённого автоподтверждения неявных транзакций);

выполнение подтверждения транзакции после серии операций (аналог выключенного автоподтверждения неявных транзакций)

Учитывая этот факт, мы реализуем решение для Oracle по аналогии с MySQL

иMS SQL Server, но без определения текущего режима автоподтверждения транзакций.

Поскольку взаимодействие Oracle с клиентским ПО всегда происходит в режиме транзакции (т.е. выполнение любого выражения по выборке или модифика-

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

Пример 41: управление неявными транзакциями

ции данных происходит внутри транзакции), в начале работы нашей хранимой процедуры мы должны выполнить операцию COMMIT (строка 12), чтобы завершить текущую транзакцию (если она есть).

В строках 16-27 мы выполняем цикл вставки, в котором мы можем явно инициировать подтверждение вставки каждой отдельной записи (строка 24), если нам нужно эмулировать режим автоподтверждения неявных транзакций. Если такая эмуляция не нужна, то все выполненные в цикле вставки подтверждаются как набор операций (строка 35).

 

Oracle

 

Решение 6.1.1.b (код процедуры)

 

 

1

 

CREATE OR REPLACE PROCEDURE TEST_INSERT_SPEED(records_count IN INT,

 

2

 

 

use_autocommit

IN INT,

3

 

 

total_time OUT

NVARCHAR2)

4AS

5counter INT := 0;

6start_time TIMESTAMP;

7finish_time TIMESTAMP;

8diff_time INTERVAL DAY TO SECOND;

9BEGIN

10DBMS_OUTPUT.PUT_LINE('Autocommit value = ' || use_autocommit);

11DBMS_OUTPUT.PUT_LINE('Committing previous transaction...');

12COMMIT;

13DBMS_OUTPUT.PUT_LINE('Starting insert of ' || records_count ||

14

 

' records

...');

 

 

 

 

15start_time := CURRENT_TIMESTAMP;

16WHILE (counter < records_count)

17LOOP

18INSERT INTO "subscribers"

19

("s_name")

20VALUES (CONCAT('New subscriber ', (counter + 1)));

21IF (use_autocommit = 1)

22THEN

23DBMS_OUTPUT.PUT_LINE('Committing small transaction...');

24COMMIT;

25END IF;

26counter := counter + 1;

27END LOOP;

28finish_time := CURRENT_TIMESTAMP;

29DBMS_OUTPUT.PUT_LINE('Finished insert of ' || records_count ||

30

 

' records...');

31

 

 

32IF (use_autocommit = 0)

33THEN

34DBMS_OUTPUT.PUT_LINE('Committing one big transaction...');

35COMMIT;

36END IF;

37

38diff_time := finish_time - start_time;

39total_time := TO_CHAR(EXTRACT(hour FROM diff_time)) || ':' ||

40

 

TO_CHAR(EXTRACT(minute

FROM

diff_time)) || ':' ||

41

 

TO_CHAR(EXTRACT(second

FROM

diff_time ), 'fm00.000000' );

42

 

 

 

 

43DBMS_OUTPUT.PUT_LINE('Time used: ' || total_time);

44END;

Для проверки работоспособности и оценки производительности Oracle в двух режимах работы с неявными транзакциями можно использовать следующие запросы.

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

Пример 41: управление неявными транзакциями

Oracle Решение 6.1.1.b (код для проверки работоспособности)

1DECLARE

2t NVARCHAR2(100);

3BEGIN

4TEST_INSERT_SPEED(10, 1, t);

5DBMS_OUTPUT.PUT_LINE('Stored procedure has returned

6

 

The following value: ' || t);

7

 

END;

8

 

 

 

 

 

9DECLARE

10t NVARCHAR2(100);

11BEGIN

12TEST_INSERT_SPEED(10, 0, t);

13DBMS_OUTPUT.PUT_LINE('Stored procedure has returned

14

 

The following value: ' || t);

15

 

END;

На этом решение данной задачи завершено.

Задание 6.1.1.TSK.A: сравнить скорость работы представленной в решении{413} задачи 6.1.1.b{408} хранимой процедуры при вставке в обоих режимах автоподтверждения неявных транзакций для 10, 100, 1000, 10000, 100000 записей во всех трёх СУБД.

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

Пример 42: управление явными транзакциями

6.1.2. Пример 42: управление явными транзакциями

Задача 6.1.2.a{419}: создать хранимую процедуру, которая:

добавляет каждому читателю три случайных книги с датой выдачи, равной текущей дате, и датой возврата, равной «текущая дата плюс месяц»;

отменяет совершённые действия, если по итогу выполнения операции хотя бы у одного читателя на руках окажется более десяти книг.

Задача 6.1.2.b{425}: создать хранимую процедуру, которая:

изменяет все даты возврата книг на «плюс три месяца»;

отменяет совершённое действие, если по итогу выполнения операции среднее время чтения книги превысит 4 месяца.

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

Хранимая процедура выполняет указанные в условии задачи действия, в конце своей работы сигнализируя о фиксации или отмене внесённых изменений.

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

Хранимая процедура выполняет указанные в условии задачи действия, в конце своей работы сигнализируя о фиксации или отмене внесённых изменений.

Решение 6.1.2.a{419}.

Решение данной задачи для MySQL будет примечательно рассмотрением способа работы с несколькими курсорами внутри одной хранимой процедуры.

Для получения решения мы будем должны:

запустить транзакцию (строка 5);

открыть курсор для извлечения идентификаторов всех читателей (строка 15);

для каждого идентификатора читателя выполнить вложенный цикл (строки 16-55), в котором:

o открыть курсор для извлечения трёх идентификаторов случайных книг (строка 13);

o для каждого полученного идентификатора книги произвести вставку в таблицу выдач книг (строки 33-51);

o закрыть курсор для извлечения трёх идентификаторов случайных книг (строка 52);

закрыть курсор для извлечения идентификаторов всех читателей (строка 56);

проверить, было ли нарушено условие о недопустимости нахождения на руках у одного читателя более десяти книг (строки 58-70) и:

o если условие было нарушено, отменить транзакцию (строка 66);

o если условие не было нарушено, подтвердить транзакцию (строка 69).

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

Обратите внимание, что конструкция DECLARE CONTINUE HANDLER FOR NOT FOUND SET не подразумевает указание имени курсора, т.е. предполагается, что он

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