Бази даних-20210115T104840Z-001 / Реферат на тему _Современные СУБД_ / Using_MySql,_MS_SQL_Server_and_Oracle
.pdfПример 38: выполнение динамических запросов с помощью хранимых процедур
ления решена, остаётся только создать хранимую процедуру по аналогии с реше-
ниями для MySQL и MS SQL Server.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 410/545
Пример 38: выполнение динамических запросов с помощью хранимых процедур
Oracle і Решение 5.2.1 .b (альтернативное решение в виде одной хранимой функции)
1CREATE OR REPLACE TYPE ........................"Show^table^objects^row" AS
2........... OBJECT
3 |
( |
|
|
|
|
"field_a" VARCHAR2(500), |
|
||||
4 |
|
||||
"field_b" VARCHAR2(32767) |
|
||||
5 |
|
||||
); |
|
|
|
|
|
6 |
|
|
|
|
|
/ |
|
|
|
|
|
7 |
|
|
|
|
|
CREATE TYPE |
"show_table_objects_table" |
|
|||
8 |
|
||||
IS TABLE OF |
"show_table_objects_row"; |
|
|||
9 |
|
||||
/ |
|
|
|
|
|
10 |
|
|
|
|
|
|
|
|
|
|
|
11 |
|
CREATE OR REPLACE FUNCTION SHOW_TABLE_OBJECTS_FNC(table_name |
IN |
||
|
|
||||
12 |
VARCHAR2) |
|
|
|
|
13 |
RETURN |
|
|
"show_table_objects_table" PIPELINED |
|
|
|
|
|
||
14 |
AS |
|
|
|
|
|
|
|
|
|
|
15 |
TYPE |
type_rc |
IS REF CURSOR; |
|
|
16 |
|
||||
rc |
type_rc |
|
|
||
|
|
|
17field_a VARCHAR2(500);
18field_b VARCHAR2(32767);
19query_text VARCHAR2(1000);
20BEGIN
21query_text := '
22SELECT ''foreign_key''AS "object_type",
23CONSTRAINT_NAME AS "object_name"
24FROM ALL_CONSTRAINTS
25WHERE OWNER = USER
26AND TABLE_NAME = ''_FP_TABLE_NAME_PLACEHOLDER_''
27 |
AND |
CONSTRAINT_TYPE = ''R'' ' |
||
28 |
||||
UNION |
|
|
||
|
|
|
||
29 |
SELECT ''trigger'' |
AS |
"object_type", |
30TRIGGER_NAME AS "object_name"
31FROM ALL_TRIGGERS
32WHERE OWNER = USER
33AND TABLE_NAME = ''_FP_TABLE_NAME_PLACEHOLDER_''';
34 |
query_text := REPLACE query_text |
'_FP_TABLE_NAME_PLACEHOLDER_', |
|
35 |
|||
table_name ; |
|||
36 |
|||
|
|
||
37 |
OPEN rc FOR query_text; |
|
|
38 |
LOOP |
|
|
|
|
||
39 |
FETCH rc INTO field_a, field_b; |
|
|
|
|
||
40 |
EXIT WHEN rc%NOTFOUND; |
|
|
41 |
PIPE ROW("show_table_objects_row" field_a, field_b ); |
||
|
42 |
END LOOP; |
|
|
43 |
CLOSE rc; |
|
|
44 |
|
45query_text := '
46SELECT VIEW_NAME,
47TEXT
48FROM ALL_VIEWS
49WHERE OWNER = USER';
51OPEN rc FOR query_text;
52LOOP
53FETCH rc INTO field_a, field_b;
54EXIT WHEN rc%NOTFOUND;
55IF (INSTR field_b, '"' || table_name || '"') > 0)
56THEN
57PIPE ROW "show_table_objects_row" 'view', field_a );
58END IF;
59END LOOP;
60CLOSE rc;
61
62 RETURN; END;
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 412/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 Стр: 413/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.
В начале каждых суток запускается созданная хранимая процедура. Все таблицы базы данных приводятся в оптимизированное состояние.
уЦ7
■ч. Решение 5.2.2.a{388}.
Основной запрос (строки 14-29), выполняющий обновление данных в таблице books_statistics, построен на основе решения{216} задачи 3.1.2.a{215}.
Однако для повышения надёжности мы будем проверять существование таблицы books_statistics, которую собираемся обновить. Эта проверка реализована в строках 5-13. Если искомая таблица не существует, мы завершаем работу хранимой процедуры, возвращая соответствующее сообщение об ошибке.
MySQL I |
Решение 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 Стр: 414/545
Пример 39: оптимизация производительности с помощью хранимых процедур
MySQL I |
Решение 5.2.2.a (код процедуры) (продолжение) |
| |
||
14 |
UPDATE |
'books statistics' |
|
|
15 |
JOIN |
|
|
|
16 |
(SELECT IFNULL('total', 0) AS 'total', |
|
||
17 |
|
IFNULL('given', 0) AS 'given', |
|
|
18 |
|
IFNULL('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') |
|
24 |
|
AS 'prepared data' |
|
|
25 |
) AS 'src' |
|
|
|
26 |
SET |
|
|
|
27 |
'books statistics' 'total' = 'src' 'total', |
|
||
28 |
'books statistics' 'given' = 'src' 'given', |
|
||
29 |
'books statistics' 'rest' = 'src' 'rest'; |
|
||
30 |
END; |
|
|
|
31 |
$$ |
|
|
|
32 |
DELIMITER ; |
|
|
|
|
|
|
|
|
Проверим работоспособность (предварительно можно выполнить отдельный запрос на обновление данных в таблице 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 (установка запуска по расписанию) |
1SET GLOBAL event_scheduler = ON;
CREATE 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 Стр: 415/545
Пример 39: оптимизация производительности с помощью хранимых процедур
MS SQL I |
Решение 5.2.2.a (код процедуры) |
| |
1CREATE PROCEDURE UPDATE BOOKS STATISTICS
2AS
3IF (NOT EXISTS(SELECT *
4 |
FROM [information schema] [tables] |
5 |
WHERE [table catalog] = DB NAME() |
6 |
AND [table name] = 'books statistics')) |
7BEGIN
8RAISERROR ('The [books statistics] table is missing.', 16, 1);
9RETURN;
10END;
11
12UPDATE [books statistics]
13SET
14[books statistics] [total] = [src] [total],
15[books statistics] [given] = [src] [given],
16[books statistics] [rest] = [src] [rest]
17FROM [books statistics]
18JOIN
19(SELECT ISNULL([total], 0 AS [total]
20ISNULL([given], 0 AS [given]
21ISNULL([total] - [given], 0) AS [rest]
22 |
FROM |
(SELECT (SELECT SUM [b |
quantity] |
|
|
23 |
|
FROM |
[books]) |
AS [total] |
|
24 |
|
(SELECT COUNT([sb book]) |
|
||
25 |
|
FROM |
[subscriptions] |
|
|
26 |
|
WHERE |
[sb is |
active] = 'Y') AS [given] |
27AS [prepared data]
28) AS [src]
29ON 11
30GO
Проверим работоспособность (предварительно можно выполнить отдельный запрос на обновление данных в таблице books_statistics и установить все значения в ноль).
MS SQL Решение 5.2.2.a (запуск и проверка работоспособности)
1 EXECUTE UPDATE_BOOKS_STATISTICS
Установка запуска полученной хранимой процедуры по расписанию в MS SQL Server не только выглядит куда более сложно, но и не даст эффекта, если вы используете Express Edition этой СУБД: в этой версии отсутствует компонент SQL Server Agent, который и отвечает за выполнение запланированных задач.
По каждой приведённой далее команде в официальной документации написано очень много (перед каждой командой специально приведены ссылка на соответствующий раздел документации), но если выразить простыми словами алгоритм, получится следующее. Необходимо:
•Создать задачу.
•Создать шаг задачи, описывающий запуск нашей хранимой процедуры.
•Создать расписание, в котором указать требуемую периодичность выполнения.
•Прикрепить ранее созданную задачу к только что созданному расписанию.
•Передать созданную задачу на обработку в SQL Server Agent.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 416/545
Пример 39: оптимизация производительности с помощью хранимых процедур
MS |
QL | |
решение 5.2.2.a (установка запуска по расписанию) |
І |
|
|||
1 |
USE msdb ; GO -- https://msdn.microsoft.com/en- |
||
2 |
us/library/ms182079.aspx EXEC dbo sp_add_j ob |
3@job_name = N'Hourly [books_statistics] update';
4GO
5-- https://msdn.microsoft.com/en-us/library/ms187358.aspx
6EXEC sp_add_j obstep
7@job_name = N'Hourly [books_statistics] update',
8@step_name = N'Execute UPDATE_BOOKS_STATISTICS stored procedure',
9@subsystem = N'TSQL',
10@command = N'EXECUTE UPDATE_BOOKS_STATISTICS',
11@database_name = N'library_ex_2015_mod'; GO
12-- https://msdn.microsoft.com/en-us/library/ms187320.aspx
13EXEC dbo sp_add_schedule
14@schedule_name = N'UpdateBooksStatistics',
15@freq_type = 4,
16@freq_interval = 4
17@freq_subday_type = 8,
18@freq_subday_interval = 1,
19@active_start_time = 000100 ;
20USE msdb ; GO -- https://msdn.microsoft.com/en-
21us/library/ms186766.aspx EXEC sp_attach_schedule
22@job_name = N'Hourly [books_statistics] update',
23@schedule_name = N'UpdateBooksStatistics'; GO
24-- https://msdn.microsoft.com/en-us/library/ms178625.aspx
25EXEC dbo sp_add_j observer
26@job_name = N'Hourly [books_statistics] update';
27GO
28
29
30
31
32
33
Убедиться, что соответствующая задача добавлена в планировщик, можно выполнив следующий запрос (он покажет список задач даже в MS SQL Server Express Edition):
MS SQL Решение 5.2.2.a (просмотр расписания)
1SELECT * FROM msdb dbo sysschedules
Вданном обсуждении24 представлен очень хороший готовый SQL-скрипт для получения в удобной форме информации обо всех запланированных в MS SQL Server задачах.
На этом решение для MS SQL Server завершено.
Переходим к Oracle. Логика работы хранимой процедуры здесь полностью эквивалентна решениям для MySQL и MS SQL Server: мы проверяем наличие таблицы books_statistics в строках 5-15 (и завершаем работу хранимой процедуры, если таблицы нет), а в строках 17-27 выполняем запрос, обновляющий данные.
24 http://www.sqlservercentral.com/Forums/Topic410557-116-1.aspx
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 417/545
Пример 39: оптимизация производительности с помощью хранимых процедур
Oracl |
і |
Решение 5.2.2.a (код процедуры) |
| |
|
|||
e |
|
||||||
|
|
|
|
|
|
|
|
1 |
CREATE OR REPLACE PROCEDURE UPDATE BOOKS STATISTICS |
||||||
2 |
AS |
|
|
|
|
|
|
3 |
rows count NUMBER; |
|
|
|
|||
4 |
BEGIN |
|
|
|
|
|
|
5 |
SELECT COUNT 1 |
INTO rows count |
|
||||
6 |
FROM ALL TABLES |
|
|
|
|
||
7 |
WHERE OWNER = USER |
|
|
|
|||
8 |
AND TABLE_NAME = 'books_statistics'; |
||||||
|
|||||||
9 |
|
|
|
|
|
|
|
10 |
IF (rows count = 0 |
|
|
|
|||
11 |
|
THEN |
|
|
|
|
|
12 |
|
RAISE APPLICATION ERROR(-20001 |
|
||||
13 |
|
|
|
|
|
'The "books statistics" table is missing.'); |
|
14 |
|
RETURN; |
|
|
|
|
|
15 |
|
END IF; |
|
|
|
|
|
16 |
|
|
|
|
|
|
|
17 |
UPDATE "books statistics" |
|
|
||||
18 |
|
SET ("total" |
"given", "rest") = |
|
|||
19 |
|
(SELECT NVL "total", 0) AS "total", |
|||||
20 |
|
|
NVL "given", 0) AS "given", |
||||
21 |
|
|
NVL "total" - "given", 0 |
AS "rest" |
|||
22 |
|
FROM |
(SELECT (SELECT SUM("b quantity" |
||||
23 |
|
|
|
FROM |
"books" |
AS "total", |
|
24 |
|
|
|
(SELECT COUNT "sb book" |
|||
25 |
|
|
|
FROM |
"subscriptions" |
||
26 |
|
|
|
WHERE |
"sb is active" = 'Y') AS "given" |
||
27 |
|
|
FROM dual |
"prepared data"); |
|||
28 |
END; |
|
|
|
|
|
|
29 |
/ |
|
|
|
|
|
|
Проверим работоспособность (предварительно можно выполнить отдельный запрос на обновление данных в таблице books_statistics и установить все значения в ноль).
Oracle |
Решение 5.2.2.a (запуск и проверка работоспособности) |
|
1 EXECUTE UPDATE_BOOKS_STATISTICS
Установка запуска полученной хранимой процедуры по расписанию выглядит следующим образом.
Oracle |
Решение 5.2.2.a (установка запуска по расписанию) |
|
1 |
BEGIN |
|
2 |
DBMS SCHEDULER.CREATE JOB ( |
|
3 |
job_name |
=> 'hourly update books statistics', |
4 |
job_type |
=> 'STORED PROCEDURE', |
5 |
job_action |
=> 'UPDATE BOOKS STATISTICS', |
6 |
start_date |
=> '01-APR-16 1.00.00 AM', |
7 |
repeat_interval |
=> 'FREQ=HOURLY;INTERVAL=1', |
8 |
auto_drop |
=> FALSE |
9 |
enabled |
=> TRUE); |
10 |
END; |
|
|
|
|
Убедиться, что соответствующая задача добавлена в планировщик, можно выполнив следующий запрос:
Oracle Решение 5.2.2.a (просмотр расписания)
1 SELECT * FROM ALL_SCHEDULER_JOBS WHERE OWNER=USER
На этом решение данной задачи завершено.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 418/545
Пример 39: оптимизация производительности с помощью хранимых процедур
Решение 5.2.2. b{388}.
Решение этой задачи одновременно является очень простым и очень сложным, т.к. нам нужно получить список таблиц базы данных и... что-то с ними сделать.
Вопрос оптимизации производительности баз данных и СУБД заслуживает отдельной книги, потому здесь мы пойдём по пути наименьшего сопротивления и будем считать, что:
•Для MySQL будет достаточно выполнить OPTIMIZE для всех таблиц.
•Для MS SQL Server достаточно выполнить REORGANIZE или REBUILD для
всех кластерных индексов (что приводит к оптимизации соответствующей таблицы, на которых построен индекс).
•Для Oracle будет достаточно выполнить SHRINK SPACE COMPACT CASCADE для всех таблиц.
(В Ещё раз особо подчеркнём: решение данной задачи носит исключительно демонстрационный характер и не должно рассматриваться как рекомендация по универсальной оптимизации производительности. В некоторых случаях выполнение показанных ниже действий может снизить
производительность базы данных, потому обязательно внимательно изучите официальную документацию по соответствующей СУБД и профессиональные рекомендации по оптимизации производительности в той или иной реальной ситуации.
Традиционно начинаем с MySQL.
Решение 5.2.2.b
1DELIMITER $$
2CREATE PROCEDURE OPTIMIZE ALL TABLES()
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 |
|
|
13 |
OPEN all tables cursor |
|
14 |
|
|
15 |
tables loop: LOOP |
|
16 |
FETCH all tables cursor INTO tbl name |
|
17 |
IF done THEN |
|
18 |
LEAVE tables loop; |
|
19 |
END IF; |
|
20 |
|
|
21SET Stable opt query = CONCAT('OPTIMIZE TABLE '', tbl name, •'•);
22PREPARE table opt stmt FROM Stable opt query
23EXECUTE table opt stmt;
24DEALLOCATE PREPARE table opt stmt
25
26 END LOOP tables loop;
27
28CLOSE all tables cursor;
29END;
30$$
31
32 DELIMITER ;
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 419/545