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

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

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

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

1

 

CREATE OR REPLACE PROCEDURE SHOW_TABLE_OBJECTS(table_name IN VARCHAR2,

2

 

final_rc OUT SYS_REFCURSOR)

3IS

4query_text VARCHAR2(1000);

5BEGIN

6query_text := '

7SELECT ''foreign_key'' AS "object_type",

8CONSTRAINT_NAME AS "object_name"

9FROM ALL_CONSTRAINTS

10WHERE OWNER = USER

11AND TABLE_NAME = ''_FP_TABLE_NAME_PLACEHOLDER_''

12AND CONSTRAINT_TYPE = ''R''

13UNION

14SELECT ''trigger'' AS "object_type",

15TRIGGER_NAME AS "object_name"

16FROM ALL_TRIGGERS

17WHERE OWNER = USER

18AND TABLE_NAME = ''_FP_TABLE_NAME_PLACEHOLDER_''

19UNION

20

 

SELECT ''view''

AS "object_type",

21"VIEW_NAME" AS "object_name"

22FROM TABLE(ALL_VIEWS_VARCHAR2)

23WHERE "TEXT" LIKE ''%"_FP_TABLE_NAME_PLACEHOLDER_"%''';

24

25 query_text := REPLACE(query_text, '_FP_TABLE_NAME_PLACEHOLDER_', 26 table_name);

27

28OPEN final_rc FOR query_text;

29END;

30/

Но одно неудобство остаётся: чтобы получить результат работы такой процедуры, придётся использовать достаточно нетривиальный код:

Oracle

Решение 5.2.1.b (код для выполнения хранимой процедуры и получения результата её работы)

1DECLARE

2rc SYS_REFCURSOR;

3object_type VARCHAR2(500);

4object_name VARCHAR2(500);

5BEGIN

6SHOW_TABLE_OBJECTS('subscriptions', rc);

7

8LOOP

9FETCH rc INTO object_type, object_name;

10EXIT WHEN rc%NOTFOUND;

11DBMS_OUTPUT.PUT_LINE(object_type || ' | ' || object_name);

12END LOOP;

13CLOSE rc;

14END;

Идаже с таким нетривиальным кодом мы получаем результат в виде текста,

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

Чуть выше мы создавали такую функцию для конвертации типа данных поля, в котором хранится текст представления. Сейчас мы доработаем её, получив в виде одной функции законченное решение, полностью удовлетворяющее условиям текущей задачи.

Заодно мы изменим тип функции на PIPELINED, что снизит нагрузку на опе-

ративную память и немного повысит производительность.

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

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

Oracle Решение 5.2.1.b (альтернативное решение в виде одной хранимой функции)

1CREATE OR REPLACE TYPE "show_table_objects_row" AS OBJECT

2(

3"field_a" VARCHAR2(500),

4"field_b" VARCHAR2(32767)

5);

6/

7CREATE TYPE "show_table_objects_table"

8IS TABLE OF "show_table_objects_row";

9/

10

11CREATE OR REPLACE FUNCTION SHOW_TABLE_OBJECTS_FNC(table_name IN VARCHAR2)

12RETURN "show_table_objects_table" PIPELINED

13AS

14TYPE type_rc IS REF CURSOR;

15rc type_rc;

16

 

field_a

VARCHAR2(500);

17

 

field_b

VARCHAR2(32767);

 

 

 

 

18query_text VARCHAR2(1000);

19BEGIN

20query_text := '

21SELECT ''foreign_key'' AS "object_type",

22CONSTRAINT_NAME AS "object_name"

23FROM ALL_CONSTRAINTS

24WHERE OWNER = USER

25AND TABLE_NAME = ''_FP_TABLE_NAME_PLACEHOLDER_''

26AND CONSTRAINT_TYPE = ''R''

27UNION

28SELECT ''trigger'' AS "object_type",

29TRIGGER_NAME AS "object_name"

30FROM ALL_TRIGGERS

31WHERE OWNER = USER

32AND TABLE_NAME = ''_FP_TABLE_NAME_PLACEHOLDER_''';

33query_text := REPLACE(query_text, '_FP_TABLE_NAME_PLACEHOLDER_',

34

 

table_name);

35

 

 

36OPEN rc FOR query_text;

37LOOP

38FETCH rc INTO field_a, field_b;

39EXIT WHEN rc%NOTFOUND;

40PIPE ROW("show_table_objects_row"(field_a, field_b));

41END LOOP;

42CLOSE rc;

43

44query_text := '

45SELECT VIEW_NAME,

46TEXT

47

 

FROM

ALL_VIEWS

48

 

WHERE

OWNER = USER';

49

 

 

 

50OPEN rc FOR query_text;

51LOOP

52FETCH rc INTO field_a, field_b;

53EXIT WHEN rc%NOTFOUND;

54IF (INSTR(field_b, '"' || table_name || '"') > 0)

55THEN

56PIPE ROW("show_table_objects_row"('view', field_a));

57END IF;

58END LOOP;

59CLOSE rc;

60

61RETURN;

62END;

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

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

Ключевая идея этой функции состоит в том, чтобы передавать на выход данные, полученные в отдельности из двух разных запросов.

Первый запрос (строки 20-42) просто выбирает данные по внешним ключам и триггерам без каких-то особых сложностей и нюансов: в переменную field_a помещается строковая константа ('foreign_key', 'trigger'), в переменную field_b — имя соответствующего внешнего ключа или триггера. Затем эти переменные используются для инициализации полей объекта show_table_objects_row.

Во втором запросе (строки 44-59) мы с использованием тех же переменных

итого же объекта, что и в первом запросе, делаем следующее:

сначала в переменные field_a и field_b помещаются имя и текст представления соответственно (строка 52);

затем значение переменной field_b используется для проверки того факта,

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

наконец (в строке 56) мы используем текстовую константу 'view' и имя представления, хранящееся в переменной field_b, для инициализации полей объекта show_table_objects_row.

Теперь мы можем получить необходимый нам результат в виде таблицы.

Oracle Решение 5.2.1.b (код для выполнения хранимой функции и получения результата в виде таблицы)

1 SELECT * FROM TABLE(SHOW_TABLE_OBJECTS_FNC('subscriptions'));

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

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

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

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

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

5.2.2.Пример 39: оптимизация производительности с помощью хранимых процедур

Задача 5.2.2.a{388}: создать хранимую процедуру, запускаемую по расписанию каждый час и обновляющую данные в агрегирующей таблице books_statistics (см. задачу 3.1.2.a{215}).

Задача 5.2.2.b{393}: создать хранимую процедуру, запускаемую по расписанию каждый день и оптимизирующую (дефрагментирующую, компактифицирующую) все таблицы базы данных.

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

Вначале каждого часа запускается созданная хранимая процедура. Данные

втаблице books_statistics приводятся в актуальное состояние.

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

В начале каждых суток запускается созданная хранимая процедура. Все таблицы базы данных приводятся в оптимизированное состояние.

Решение 5.2.2.a{388}.

Основной запрос (строки 14-29), выполняющий обновление данных в таблице books_statistics, построен на основе решения{216} задачи 3.1.2.a{215}.

Однако для повышения надёжности мы будем проверять существование таблицы books_statistics, которую собираемся обновить. Эта проверка реализована в строках 5-13. Если искомая таблица не существует, мы завершаем работу хранимой процедуры, возвращая соответствующее сообщение об ошибке.

MySQL Решение 5.2.2.a (код процедуры)

1DELIMITER $$

2CREATE PROCEDURE UPDATE_BOOKS_STATISTICS()

3BEGIN

4

 

 

 

5

 

IF (NOT EXISTS(SELECT *

 

6

 

FROM `information_schema`.`tables`

7

 

WHERE `table_schema` =

DATABASE()

8

 

AND `table_name`

= 'books_statistics'))

9THEN

10SIGNAL SQLSTATE '45001'

11SET MESSAGE_TEXT = 'The `books_statistics` table is missing.',

12MYSQL_ERRNO = 1001;

13END IF;

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

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

MySQL

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

14UPDATE `books_statistics`

15JOIN

16(SELECT IFNULL(`total`, 0) AS `total`,

17IFNULL(`given`, 0) AS `given`,

18IFNULL(`total` - `given`, 0) AS `rest`

19

 

FROM

(SELECT (SELECT

SUM(`b_quantity`)

 

 

20

 

 

FROM

`books`)

AS

`total`,

21

 

 

(SELECT

COUNT(`sb_book`)

 

 

22

 

 

FROM

`subscriptions`

 

 

23

 

 

WHERE

`sb_is_active` = 'Y') AS

`given`)

24AS `prepared_data`

25) AS `src`

26SET

27`books_statistics`.`total` = `src`.`total`,

28`books_statistics`.`given` = `src`.`given`,

29`books_statistics`.`rest` = `src`.`rest`;

30END;

31$$

32DELIMITER ;

Проверим работоспособность (предварительно можно выполнить отдельный запрос на обновление данных в таблице books_statistics и установить все значения в ноль).

MySQL

Решение 5.2.2.a (запуск и проверка работоспособности)

1 CALL UPDATE_BOOKS_STATISTICS;

Установка запуска полученной хранимой процедуры по расписанию выглядит следующим образом. Предварительно в строке 1 мы включаем планировщик задач MySQL (для того, чтобы он продолжал работать и после перезапуска MySQL, необходимо добавить строку event_scheduler = on в файл настроек MySQL my.ini).

 

MySQL

Решение 5.2.2.a (установка запуска по расписанию)

 

1

 

SET GLOBAL event_scheduler = ON;

 

2

 

 

 

3CREATE EVENT `update_books_statistics_hourly`

4ON SCHEDULE

5EVERY 1 HOUR

6STARTS DATE(NOW()) + INTERVAL (HOUR(NOW())+1) HOUR + INTERVAL 1 MINUTE

7ON COMPLETION PRESERVE

8DO

9CALL UPDATE_BOOKS_STATISTICS;

Убедиться, что соответствующая задача добавлена в планировщик, можно выполнив следующий запрос:

MySQL Решение 5.2.2.a (просмотр расписания)

1 SELECT * FROM `information_schema`.`events`

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

Переходим к MS SQL Server. Логика работы хранимой процедуры здесь полностью эквивалентна решению для MySQL: мы проверяем наличие таблицы books_statistics в строках 3-10 (и завершаем работу хранимой процедуры, если таблицы нет), а в строках 12-30 выполняем запрос, обновляющий данные.

Пока всё выглядит достаточно просто и тривиально, но добавление задачи в планировщик в данной СУБД реализовано куда более сложным образом.

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

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