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

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

Пример 47: формирование и анализ связанных структур

7.1.2. Пример 47: формирование и анализ связанных структур

Для решения задач из данного примера нам понадобится новая таблица, которую мы создадим в базе данных «Исследование». Эта таблица будет хранить граф (допустим, что это будет информация о стоимости доставки книг из одного города в другой для организации сотрудничества с другими библиотеками).

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

Поле cn_bidir в таблице connections является признаком того, что стои-

мость доставки одинакова как при отправке книги из города cn_from в город cn_to,

так и из cn_to в cn_from.

dm MySQL

dm SQLServ er2012

dm Oracle

cities

«column» *PK ct_id: INT

*ct_name: VARCHAR(50)

«PK»

+PK_cities(INT)

+PK_cities

1

+PK_cities

1

(cn_from = ct_id)

(cn_to = ct_id)

«FK»

«FK»

+FK_connections_cities1

+FK_connections_cities2

 

0..*

 

 

0..*

 

connections

 

«column»

 

 

*pfK cn_from: INT

 

*pfK cn_to: INT

 

 

cn_cost: DOUBLE

 

*

cn_bidir: ENUM = ('N','Y')

«FK»

+FK_connections_cities1(INT)

+FK_connections_cities2(INT)

«PK»

+PK_connections(INT, INT)

cities

«column» *PK ct_id: int

*ct_name: nvarchar(50)

«PK»

+

PK_cities(int)

 

+PK_cities

1

+PK_cities

1

(cn_from = ct_id)

(cn_to = ct_id)

«FK»

«FK»

+FK_connections_cities1+FK_connections_cities2

 

0..*

 

0..*

 

connections

 

«column»

 

 

*pfK cn_from: int

 

 

*pfK cn_to: int

 

 

cn_cost: money

 

*cn_bidir: char(1)

«FK»

+FK_connections_cities1(int)

+FK_connections_cities2(int)

«PK»

+PK_connections(int, int)

«check»

+CHK_bidir(char)

cities

«column» *PK ct_id: NUMBER(10)

*ct_name: NVARCHAR2(50)

«PK»

+PK_cities(NUMBER)

+PK_cities

1

+PK_cities

1

(cn_from = ct_id)

(cn_to = ct_id)

«FK»

«FK»

+FK_connections_cities1+FK_connections_cities2

 

0..*

 

0..*

 

connections

 

«column»

 

 

 

*pfK cn_from: NUMBER(10)

 

*pfK cn_to: NUMBER(10)

 

cn_cost: NUMBER(15,4)

 

*cn_bidir: CHAR(1)

«FK»

+FK_connections_cities1(NUMBER)

+FK_connections_cities2(NUMBER)

«PK»

+PK_connections(NUMBER, NUMBER)

«check»

+CHK_bidir(CHAR)

MySQL

MS SQL Server

Oracle

Рисунок 7.c — Таблицы cities и connections во всех трёх СУБД

Сохраним в таблице cities следующий набор данных.

ct_id

ct_name

1

Лондон

2

Париж

3

Мадрид

4

Токио

5

Москва

6

Киев

7

Минск

8

Рига

9

Варшава

10

Берлин

41 http://www.amazon.com/dp/1558609202/

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

Пример 47: формирование и анализ связанных структур

Сохраним в таблице connections следующий набор данных.

cn_from

cn_to

cn_cost

cn_bidir

1

5

10

Y

1

7

20

N

7

1

25

N

7

2

15

Y

2

6

50

N

6

8

40

Y

8

4

30

N

4

8

35

N

8

9

15

Y

9

1

20

N

7

3

5

N

3

6

5

N

Теперь, когда все данные подготовлены, мы можем переходить к задачам.

Задача 7.1.2.a{492}: доработать модель базы данных таким образом, чтобы для прямых маршрутов (без пересадок), цена перемещения по которым «туда» и «обратно» одинакова, в запросе на поиск такого маршрута можно было произвольно менять местами точки отправки и назначения.

Задача 7.1.2.b{493}: написать хранимую процедуру, проверяющую существование маршрута (с возможными пересадками) между двумя указанными городами, и вычисляющую стоимость отправки книги по такому маршруту (при его наличии).

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

Запрос вида

1

SELECT

*

2

FROM

{источник_данных}

3

WHERE

{откуда} = 5

4

AND

{куда} = 1

должен возвращать такой результат (обратите внимание: в представленных выше данные нет маршрута из города 5 в город 1, есть только из 1 в 5, но этот маршрут

— двунаправленный):

cn_from

cn_to

cn_cost

cn_bidir

5

1

10

Y

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

Например, для городов с идентификаторами 1 и 6 хранимая процедура должна возвратить такие данные.

cn_from

cn_to

cn_cost

cn_bidir

cn_steps

cn_route

1

6

31

N

3

1,7,3,6

1

6

85

N

3

1,7,2,6

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

Пример 47: формирование и анализ связанных структур

Решение 7.1.2.a{491}.

Для решения этой задачи нам необходимо обеспечить такое поведение СУБД, чтобы для двунаправленных маршрутов при указании условия поиска как cn_from=A ADN cn_to=B в выборку попадали также и маршруты, для которых выполняется условие cn_from=B AND cn_to=A. Проще всего такого эффекта можно добиться с использованием представлений.

В строке 8 представленного ниже кода для MySQL можно было не писать ключевое слово DISTINCT (т.к. по умолчанию (без ключевого слова ALL) оператор UNION работает в DISTINCT-режиме), но оно там есть для наглядности, чтобы подчеркнуть необходимость устранения дублирующихся записей.

MySQL Решение 7.1.2.a

1CREATE OR REPLACE VIEW `connections_bidir`

2AS

3SELECT `cn_from`,

4`cn_to`,

5`cn_cost`,

6`cn_bidir`

7 FROM `connections`

8UNION DISTINCT

9SELECT `cn_to`,

10`cn_from`,

11`cn_cost`,

12`cn_bidir`

13

 

FROM

`connections`

14

 

WHERE

`cn_bidir` = 'Y'

На примере решения для MySQL рассмотрим подробно, как работает такое представление. Если выбрать из него все данные, получится следующая картина. Серым фоном отмечены строки, появившиеся в результате выполнения UNION- части запроса: для всех двунаправленных маршрутов добавились записи с инвертированными пунктами отправки и назначения.

cn_from

cn_to

cn_cost

cn_bidir

1

5

10

Y

1

7

20

N

2

6

50

N

3

6

6

N

4

8

35

N

6

8

40

Y

7

1

25

N

7

2

15

Y

7

3

5

N

8

4

30

N

8

9

15

Y

9

1

20

N

5

1

10

Y

8

6

40

Y

2

7

15

Y

9

8

15

Y

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

Пример 47: формирование и анализ связанных структур

Теперь выполнение запроса

MySQL Решение 7.1.2.a (проверка работоспособности)

1

SELECT

*

2

FROM

`connections_bidir`

3

WHERE

`cn_from` = 5

4

 

AND `cn_to` = 1

вернёт корректный ожидаемый результат.

cn_from

cn_to

cn_cost

cn_bidir

5

1

10

Y

В MS SQL Server и Oracle синтаксис оператора UNOIN не допускает явного указания слова DISTINCT (строка 8 двух показанных ниже запросов), но это — не проблема, т.к. по умолчанию (без ключевого слова ALL) оператор UNION работает в DISTINCT-режиме.

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

1CREATE VIEW [connections_bidir]

2AS

3SELECT [cn_from],

4[cn_to],

5[cn_cost],

6[cn_bidir]

7 FROM [connections]

8UNION

9SELECT [cn_to],

10[cn_from],

11[cn_cost],

12[cn_bidir]

13

 

FROM

[connections]

14

 

WHERE

[cn_bidir] = 'Y'

Oracle Решение 7.1.2.a

1CREATE OR REPLACE VIEW "connections_bidir"

2AS

3SELECT "cn_from",

4"cn_to",

5"cn_cost",

6"cn_bidir"

7 FROM "connections"

8UNION

9SELECT "cn_to",

10"cn_from",

11"cn_cost",

12"cn_bidir"

13

 

FROM

"connections"

14

 

WHERE

"cn_bidir" = 'Y'

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

Решение 7.1.2.b{491}.

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

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

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

Пример 47: формирование и анализ связанных структур

Впервом варианте реализуется следующий подход42:

в оперативной памяти (ENGINE = HEAP) создаётся временная таблица для хранения найденных путей (строки 10-18);

в созданную таблицу переносятся все данные из таблицы connections с учётом двунаправленности некоторых связей (строки 21-40; аналогичный подзапрос, учитывающий двунаправленные связи, используется в строках 68-80 — фактически, он представляет собой ничто иное, как тело представления из решения{492} задачи 7.1.2.a{491});

выполняется цикл поиска производных маршрутов (строки 45-92), в котором: o условием выхода является отсутствие новых маршрутов (функция MySQL ROW_COUNT возвращает количество записей, затронутых по-

следней операцией модификации данных);

oидея поиска новых маршрутов строится на том, чтобы к уже найденным маршрутам добавлять следующие шаги (конечная точка найденного маршрута совпадает с отправной точкой связи между городами, что проверяется в условии объединения в строке 81; условие в строках 82-83 исключает порождение циклических маршрутов; условие в строках 88-89 исключает бесконечное повторное дублирующихся маршрутов между двумя любыми городами).

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

Уэтого решения есть два недостатка:

предварительное построение всех возможных маршрутов избыточно и приводит к бессмысленным затратам памяти и потере производительности;

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

MySQL

Решение 7.1.2.b (код процедуры, первый вариант)

1DELIMITER $$

2CREATE PROCEDURE FIND_PATH(IN start_node INT,

3

IN finish_node INT)

4BEGIN

5DECLARE rows_inserted INT DEFAULT 0;

6

7-- Пересоздание временной таблицы для хранения маршрутов

8-- (именно DROP/CREATE на случай, если такая таблица была):

9DROP TABLE IF EXISTS `connections_temp`;

10CREATE TABLE IF NOT EXISTS `connections_temp`

11(

12`cn_from` INT,

13`cn_to` INT,

14`cn_cost` DOUBLE,

15`cn_bidir` CHAR(1),

16`cn_steps` SMALLINT,

17`cn_route` VARCHAR(1000)

18) ENGINE = MEMORY;

42 https://www.artfulsoftware.com/mysqlbook/sampler/mysqled1ch20.html

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

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