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

Пример 40: управление структурами базы данных с помощью хранимых процедур

Решение 5.2.3.b{400}.

Логика решения этой задачи представляет собой комбинацию подходов, представленных в решениях*393*{400} задач 5.2.2.b{388} и 5.2.3.a{400}. Мы будем:

проверять существование целевой таблицы tables_rc;

создавать её, если таковой не обнаружилось;

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

Важно отметить, что здесь для упрощения кода мы выполняем операцию TRUNCATE с последующим добавлением рядов. Это избавляет нас от

необходимости реализовывать алгоритм с добавлением информации о новых таблицах, удалением информации о старых и обновлением информации о существующих.

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

Решение для MySQL выглядит следующим образом.

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

1DELIMITER $$

2CREATE PROCEDURE CACHE TABLES RC()

3BEGIN

4DECLARE done INT DEFAULT 0

5DECLARE tbl name VARCHAR 200) DEFAULT '';

6DECLARE all tables cursor CURSOR FOR

7SELECT 'table name'

8

FROM

'information schema' 'tables'

9

WHERE

'table

schema'

=

DATABASE()

10

AND

'table

type' =

'BASE

TABLE';

11

DECLARE CONTINUE HANDLER

FOR

NOT FOUND SET done = 1;

12

 

 

 

 

 

 

13IF NOT EXISTS

14(SELEC 'table name'

15FROM 'information schema' 'tables'

16WHERE 'table schema' = DATABASE()

17

AND

'table

type'

=

'BASE TABLE'

18

AND

'table

name' =

'tables rc')

19THEN

20CREATE TABLE 'tables rc'

21(

22'tabl name' VARCHAR(200 ,

23' count' INT

24);

25END IF;

26

 

 

 

27

TRUNCAT TABLE 'tables

rc';

28

 

 

 

29

OPEN

tables cursor

 

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

Пример 40: управление структурами базы данных с помощью хранимых процедур

MySQL I

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

|

30tables loop LOOP

31FETCH all tables cursor INTO tbl name

32IF done THEN

33LEAVE tables loop;

34END IF;

35

 

36

SET @table rc query = CONCAT('SELECT COUNT(1) INTO @tbl rc FROM ' ,

37

tbl name ’'’);

38PREPARE table opt stmt FROM @table rc query'

39EXECUTE table opt stmt;

40DEALLOCATE PREPARE table opt stmt

41

 

42

INSERT INTO 'tables rc' ('table name',

43

'rows count')

44

VALUES (tbl name

45

@tbl rc ;

46

 

47

END LOOP tables loop;

48

 

49CLOSE all tables cursor;

50END;

51$$

52DELIMITER ;

Для проверки корректности полученного решения можно выполнить следующие запросы.

MySQL

Решение 5.2.3.b (проверка работоспособности)

1

CALL CACHE_TABLES_RC;

2

SELECT * FROM 'tables rc';

Решение для MS SQL Server выглядит следующим образом.

MS SQL Решение 5.2.3.b

1CREATE PROCEDURE CACHE TABLES RC

2AS

3BEGIN

4DECLARE @table name NVARCHAR'200 ;

5DECLARE @table rows INT;

6DECLARE @query text NVARCHAR'2000 ;

7DECLARE tables cursor CURSOR LOCAL FAST FORWARD FOR

8SELECT [name]

9

FROM

sys.tables;

10

 

 

11IF NOT EXISTS

12(SELECT [name]

13 FROM sys.tables

14WHERE [name] = 'tables rc')

15BEGIN

16CREATE TABLE [tables rc]

17(

18[table name] VARCHAR 200),

19[rows count] INT

20);

21END;

22

23 TRUNCATE TABLE [tables rc];

24

25OPEN tables cursor

26FETCH NEXT FROM tables_cursor INTO @table_name;

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

Пример 40: управление структурами базы данных с помощью хранимых процедур

MS SQL I

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

|

27WHILE @@FETCH STATUS = 0

28BEGIN

29SET @query text = CONCAT('SELECT @cnt = COUNT(1) FROM [',

30

@table name ']');

31

EXECUTE sp executesql @query text, N'@cnt INT OUT', @table rows OUTPUT;

32

 

33

INSERT INTO [tables rc] ([table name],

34

[rows count])

35

VALUES @table name

36

@table rows';

37

 

38FETCH NEXT FROM tables cursor INTO @table name

39END;

40CLOSE tables cursor;

41DEALLOCATE tables cursor;

42END;

43GO

Для проверки корректности полученного решения можно выполнить следующие запросы.

MS SQL

Решение 5.2.3.b (проверка работоспособности)

2

 

SELECT * FROM [tables rc]

 

 

1

EXECUTE CACHE_TABLES_RC;

 

 

 

 

Решение для Oracle выглядит следующим образом.

Oracl

і

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

|

e

 

 

 

 

 

 

 

 

1

 

CREATE OR REPLACE PROCEDURE CACHE TABLES RC

2

 

AS

 

 

 

 

 

3

 

table name VARCHAR(150

:= '';

4

 

table rows NUMBER 10) := 0

 

5

 

table found NUMBER(1) :=0;

 

6

 

query text VARCHAR(1000) := '';

7

 

CURSOR tables cursor IS

 

 

8

 

 

SELECT TABLE NAME AS "table name"

9

 

 

FROM

ALL TABLES

 

 

10

 

 

WHERE OWNER=USER;

 

 

11

 

BEGIN

 

 

 

 

12

 

SELECT COUNT 1

INTO table found

13

 

FROM

ALL TABLES

 

 

14

 

WHERE

OWNER=USER

 

 

15

 

AND

TABLE_NAME = 'tables_rc';

16

 

 

 

 

 

 

 

17

 

IF (table found = 0)

 

 

18

 

THEN

 

 

 

 

19

 

 

EXECUTE IMMEDIATE 'CREATE TABLE "tables rc"

20

 

 

("table name" VARCHAR(200),

21

 

 

"rows count" NUMBER(10)) ' ;

22

 

END IF;

 

 

 

23

 

EXECUTE IMMEDIATE 'TRUNCATE TABLE "tables_rc"';

24

 

 

 

 

 

 

 

25FOR one row IN tables cursor

26LOOP

27query text := 'SELECT COUNT(1) FROM "' || one row "table name" ||

28

'"';

29

EXECUTE IMMEDIATE query_text INTO table_rows

30

 

31

query text := 'INSERT INTO "tables rc" ("table name", "rows count")

32

VALUES (''' || one row "table name" || ''', ' ||

33

table rows || ')';

34EXECUTE IMMEDIATE query text 1

35END LOOP;

36END;

37/

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

Пример 40: управление структурами базы данных с помощью хранимых процедур

Для проверки корректности полученного решения можно выполнить следующие запросы.

1 EXECUTE CACHE_TABLES_RC;

2 SELECT * FROM "tables rc"

Задание 5.2.3.TSK.A: создать хранимую процедуру, автоматически создающую и наполняющую данными таблицу arrears, в которой должны быть представлены идентификаторы и имена читателей, у которых до сих пор находится на руках хотя бы одна книга, по которой дата возврата установлена в прошлом относительно текущей даты. Эта таблица должна быть связана с таблицей subscriptions связью «один к одному».

Задание 5.2.3.TSK.B: создать хранимую процедуру, удаляющую все индексы (кроме первичных ключей), построенные на таблицах текущей базы данных и включающие в себя более одного поля.

& Задание 5.2.3.TSK.C: создать хранимую процедуру, удаляющую все представления, для которых SELECT COUNT(1) FROM представление

возвращает значение меньше десяти.

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

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

Раздел 6: Использование транзакций

6.1. Управление неявными и явными транзакциями

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

О

Задача 6.1.1.a{408}: продемонстрировать поведение СУБД при выполнении

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

 

ния неявных транзакций включён и выключен.

О Задача 6.1.1.b{413}: создать хранимую процедуру, выполняющую следующие действия:

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

выключающую этот режим, если требуется (если в процедуру передан соответствующий параметр с соответствующим значением);

выполняющую вставку N записей в таблицу subscribers (N передаётся в процедуру соответствующим параметром);

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

возвращающую время, затраченное на выполнение вставки.

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

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

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

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

уЦ7

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

Режим автоподтверждения неявных транзакций актуален для MySQL27 и MS SQL Server2829 (и не актуален для Oracle30, где данное поведение полностью отдано на усмотрение клиентского ПО) в случае, когда операции не обрамляются явным образом выражениями по запуску и подтверждению или отмене транзакций.

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

27http://dev.mysql.com/doc/refman/5.6/en/commit.html

28https://technet.microsoft.com/en-us/library/ms190230%28v=sql.105%29.aspx

29https://msdn.microsoft.com/en-us/library/ms187807.aspx

30https://asktom.oracle.com/pls/apex/f?p=100:11:0%3A%3A%3A%3AP11_QUESTION_ID:314816776423

Работа с MySQL, MS SQL Server и Oracle в примерах © Богдан Марчук Стр: 434/545

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

в конфигурационном файле).

Работа с MySQL, MS SQL Server и Oracle в примерах © Богдан Марчук Стр: 435/545

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

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

Решение 6.1.1 .а

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

2SET autocommit = 0;

3

 

 

4

SELECT COUNT(*)

5

FROM

'subscribers'; — 4

6

 

 

7

INSERT INTO 'subscribers'

8

 

('s name')

9

VALUES

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

10

 

 

11SELECT COUNT(*)

12FROM 'subscribers'; — 5

14

ROLLBACK;

15

 

16SELECT COUNT(*)

17FROM 'subscribers'; — 4

18

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

20SET autocommit = 1;

21

22SELECT COUNT(*)

23FROM 'subscribers'; — 4

25INSERT INTO 'subscribers'

26

 

('s name')

27

VALUES

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

28

 

 

29SELEcT COUNT(*)

30FROM 'subscribers'; — 5

31

32 ROLLBACK;

33

34SELECT COUNT(*)

35FROM 'subscribers'; — 5

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

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

MS SQL Server (как и MySQL) по умолчанию работает с включённым автоподтверждением неявных транзакций, т.е. любые изменения данных сразу же вступают в силу. За изменение данного поведения отвечает параметр IM-

PLICIT_TRANSACTIONS (которым в общем случае можно управлять только ло-

кально на протяжении сессии; общие идеи по управлению этим параметром на уроне настроек описаны здесь31).

Для решения данной задачи в MS SQL Server необходимо использовать следующий набор запросов. Обратите внимание, что параметр IMPLICIT_TRANSAC- TIONS в MS SQL Server по своей логике противоположен параметру autocommit в MySQL (т.е. для выключения автоподтверждения неявных транзакций необходимо

31https://msdn.microsoft.com/en-us/library/ms176031 %28SQL.90%29.aspx

Работа с MySQL, MS SQL Server и Oracle в примерах © Богдан Марчук Стр: 436/545

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

выполнить команду SET IMPLICIT_TRANSACTIONS ON).

Работа с MySQL, MS SQL Server и Oracle в примерах © Богдан Марчук Стр: 437/545

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

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

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

2SET IMPLICIT TRANSACTIONS ON;

4

SELECT COUNT(*)

5

FROM

[subscribers]; -- 4

6

 

 

7

INSERT INTO [subscribers]

8

 

([s name])

9

VALUES

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

10

 

 

11SELECT COUNT(*)

12FROM [subscribers]; -- 5

14

ROLLBACK;

15

 

16SELECT COUNT(*)

17FRoM [subscribers]; -- 4

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

20 SET IMPLICIT TRANSACTIONS OFF;

21

22SELECT COUNT(*)

23FRoM [subscribers]; -- 4

25INSERT INTO [subscribers]

26

 

([s name])

27

VALUES

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

28

 

 

29SELECT COUNT(*)

30FRoM [subscribers]; -- 5

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 в примерах © Богдан Марчук Стр: 438/545

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

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

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

Решение 6.1.1 .а (демонстр

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

2 SET IMPLICIT_TRANSACTIONS OFF;

3 PRINT @@TRANCOUNT; — 0

5 BEGIN TRANSACTION;

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

6PRINT @@TRANCOUNT; — 1

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

8BEGIN TRANSACTION;

9PRINT @@TRANCOUNT; — 2

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

11COMMIT TRANSACTION;

12PRINT @@TRANCOUNT; — 1

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

14COMMIT TRANSACTION;

 

15

PRINT @@TRANCOUNT; — 0

17

16

 

 

18

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

IMPLICIT_TRANSACTIONS ON;

19

PRINT @@TRANCOUNT;

0

 

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

21PRINT @@TRANCOUNT; 2

22-- Старт второй ("дочерней") транзакции BEGIN TRANSACTION;

23PRINT @@TRANCOUNT; — 3

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

25PRINT @@TRANCOUNT; — 2

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

27

PRINT @@TRANCOUNT;

1

 

28

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

 

29

PRINT @@TRANCOUNT;

0

 

30

 

 

 

 

 

31

--

Режим

по умолчанию

 

 

32

SET IMPLICIT_TRANSACTIONS OFF;

 

33

--

Старт

 

первой ("родительской")

транзакции

34

BEGIN TRANSACTION;

 

 

35

PRINT @@TRANCOUNT;

1

 

36-- Старт второй ("дочерней") транзакции BEGIN TRANSACTION;

37PRINT @@TRANCOUNT; — 2 -- Отмена всех транзакций ROLLBACK TRANSACTION;

38

PRINT @@TRANCOUNT;

0

 

39

 

 

 

40

-- Режим "неявных транзакций" SET IMPLICIT_TRANSACTIONS ON;

41

-- Старт

первой ("родительской")

транзакции

42BEGIN TRANSACTION;

43PRINT @@TRANCOUNT; 2

44-- Старт второй ("дочерней") транзакции BEGIN TRANSACTION;

45PRINT @@TRANCOUNT; — 3 -- Отмена всех транзакций ROLLBACK TRANSACTION;

46 PRINT @@TRANCOUNT;

0

47

48

49

50

51

52

53

54

55

56

57

58

Работа с MySQL, MS SQL Server и Oracle в примерах © Богдан Марчук Стр: 439/545