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

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

MySQL

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

75

 

FROM

'current_path'

 

WHERE

'cp_from' =

cn_from_value

76

 

 

AND 'cp_to' = cn_to_value

77

 

THEN

 

 

 

78

 

 

 

ITERATE nodes_loop;

 

79

 

END IF;

 

 

 

80

 

 

 

 

 

 

 

81

Такая связь приводит к циклу, пропускаем

82

IF EXISTS (SELECT 1

 

83

 

 

FROM

'current_path'

84

 

 

WHERE

'cp_from' =

cn_to_value

85

 

THEN

 

 

 

86

 

 

 

ITERATE nodes_loop;

 

87

 

END IF;

 

 

 

88

 

 

 

 

 

 

 

89

Конечная точка связи совпала с точкой финиша, путь найден

90

IF

 

cn_to_value

= finish_node

91

 

THEN

 

 

 

73

- Такая связь уже есть в текущем пути, пропускаем

74

IF EXISTS (SELECT 1

 

 

 

 

 

92

SET @rand_value = RAND();

93

 

94

INSERT INTO 'final_paths'

95

('fp_id',

96

'fp_from' ,

97

'fp_to',

98

'fp_cost',

99

'fp_bidir')

 

100SELECT @rand_value,

101'cp_from',

102'cp_to',

103'cp_cost',

104'cp_bidir'

105FROM 'current_path';

107

INSERT INTO 'final_paths'

 

108

('fp_id',

 

109

'fp_from' ,

 

110

'fp_to',

 

111

'fp_cost',

 

112

'fp_bidir')

 

113

VALUE S (@ rand_value,

 

114

cn_from_value, cn_to_value cn_cos t_value, cn_bidir_value);

 

115

ELSE

 

116

 

117

 

118

 

119

 

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

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

MySQL I

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

|

120-- Добавляем связь в текущий путь

121INSERT INTO 'current_path'

122

('cp id',

123

'cp from' ,

124

'cp to',

125

'cp

cost',

126

'cp

bidir')

127

VALUES (NULL,

128

cn from value

129

cn to value,

130

cn cost value

131

cn_bidir_value ;

132

 

133-- Продолжаем рекурсивно искать следующие связи

134CALL FIND_PATH start_node, finish_nodei;

135

136-- Удаляем последнюю связь из текущего пути

137SET @max cp id = (SELECT MAX('cp id')

138

FROM 'current_path');

139

DELETE FROM 'current_path'

140

WHERE 'cp id' = @max cp id

141END IF;

142END LOOP nodes loop;

143CLOSE nodes cursor;

144END;

145$$

146DELIMITER ;

Проверим, как работают полученные решения, выполнив такой код.

MySQL

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

1-- Для первого варианта решения:

2CALL FIND_PATH({начальная_точка}, {конечная_точка});

4 -- Для второго варианта решения:

5TRUNCATE TABLE'current_path';

6TRUNCATE TABLE 'final_paths';

CALL FIND_PATH({начальная_точка}, {конечная_точка}); 8 SELECT * FROM 'final paths';

На представленном в начале данного примера наборе данных для поиска пути из города 1 в город 6 оба решения возвращают одинаковые (хоть и по-разному представленные) результаты.

Результат первого варианта решения:

cn_from

cn_to

cn_cost

cn_bidir

cn_steps

cn_route

1

6

30

N

3

1,7,3,6

1

6

85

N

3

1,7,2,6

Результат второго варианта решения:

fp_id

fp_from

fp_to

fp_cost

fp_bidir

0.42358866466543516

1

7

20

N

0.42358866466543516

7

2

15

Y

0.42358866466543516

2

6

50

N

0.34713028031134074

1

7

20

N

0.34713028031134074

7

3

5

N

0.34713028031134074

3

6

5

N

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

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

Но если в таблицу connections поместить следующие данные

cn_from

cn_to

cn_cost

cn_bidir

1

3

100

N

1

5

100

N

3

5

20

N

5

3

200

N

и поискать путь между городами 1 и 5, результаты будут разными. Результат первого варианта решения:

cn_from

 

cn_to

cn_cost

cn_bidir

cn_steps

cn_route

1

 

5

100

 

N

1

 

1,5

 

Результат второго варианта решения:

 

 

 

fp_id

 

fp_from

fp_to

 

fp_cost

fp_bidir

 

0.6203666074391143

1

 

3

 

100

N

 

0.6203666074391143

3

 

5

 

20

N

 

0.2569376912498266

1

 

5

 

100

N

 

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

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

Переходим к MS SQL Server и реализуем один-в-один решение, представленное выше для MySQL.

Алгоритм первого варианта решения:

удаляется (если существует) и создаётся временная таблица для хранения найденных путей (строки 7-19);

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

выполняется цикл поиска производных маршрутов (строки 46-93), в котором:

оусловием выхода является отсутствие новых маршрутов (переменная

MS SQL Server @@ROWCOUNT содержит количество записей, затронутых последней операцией модификации данных);

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

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

Обратите внимание на то, как формируется и анализируется маршрут, раз-

мещаемый в поле cn_route таблицы connections_temp: в MS SQL Server нет прямого аналога функции MySQL FIND_IN_SET, а при поиске вхождения подстроки

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

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

в строку есть шанс, например, «найти» число 12 в числе 123 и т.д.

Потому каждое значение идентификатора берётся в квадратные скобки (маршрут примет вид наподобие «[1][7][3][6]»), и поиск тоже производится с предварительным заключением искомого идентификатора в квадратные скобки, что гарантирует отсутствие ложных срабатываний.

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

1

CREATE PROCEDURE FIND PATH

2

@start node INT,

3

@finish node INT

4AS

5DECLARE @rows_inserted INT = 0;

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

8IF OBJECT ID('tempdb.dbo.#connections temp', 'U') IS NOT NULL

9DROP TABLE #connections temp

10

11CREATE TABLE #connections temp

12(

13[cn from] INT,

14[cn to] INT,

15[cn cost] DOUBLE PRECISION,

16[cn bidir] CHAR 1 ,

17[cn steps] SMALLINT,

18[cn route] VARCHAR 1000 )

19);

20

21-- Первичное наполнение временной таблицы

22-- существующими маршрутами:

23INSERT INTO #connections temp

24SELECT [cn from],

25[cn to],

26[cn cost] ,

27[cn bidir]

281,

29CONCAT('[', [cn from] , '][', [cn to] , ']')

30

FROM

(SELECT [cn from] ,

31

 

 

[cn to],

32

 

 

[cn

cost],

33

 

 

[cn

bidir]

34

 

FROM

[connections]

35UNION

36SELECT [cn to],

37

 

[cn from] ,

38

 

[cn

cost],

39

 

[cn

bidir]

40

FROM

[connections]

41WHERE [cn bidir] = 'Y'

42) AS [connections bidir];

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

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

MS SQL і Решение 7.1.2.b (код процедуры, первый вариант) (продолжение) |

43— Наполнение временной таблицы производными

44— маршрутами:

45SET @rows inserted = @@ROWCOUNT;

46WHILE @rows inserted > 0)

47BEGIN

48INSERT INTO #connections temp

49SELECT [connections next] [cn from],

50

[connections next] [cn to],

 

 

51

[connections next] [cn cost],

 

 

52

[connections next] [cn bidir],

 

 

53

[connections next] [cn steps],

 

 

54

[connections next] [cn route]

 

 

55

FROM (SELECT #connections temp [cn from] AS [cn from]

 

56

[connections] [cn to] AS [cn to],

 

57

#connections temp [cn cost] +

 

 

58

[connections] [cn cost] AS [cn cost],

 

59

CASE

 

 

 

60

WHEN

#connections temp [cn bidir] = 'Y')

 

61

AND ([connections] [cn bidir] =

'Y')

 

62

THEN 'Y'

 

 

63

ELSE 'N'

 

 

64

END AS

[cn bidir],

 

 

65

#connections temp [cn steps] + 1

AS [cn steps]

 

66

CONCAT #connections temp [cn route], '[',

 

67

 

[connections] [cn to], ']') AS [cn route]

 

68

FROM #connections temp

 

 

69

JOIN (SELECT [cn from] ,

 

 

70

[cn to]

 

 

71

[cn cost],

 

 

72

[cn bidir]

 

 

73

FROM

[connections]

 

 

74

UNION

 

 

75

SELECT [cn to]

 

 

76

[cn from] ,

 

 

77

[cn cost],

 

 

78

[cn bidir]

 

 

79

FROM

[connections]

 

 

80

WHERE

[cn bidir] = 'Y'

 

 

81

) AS [connections]

 

 

82

ON #connections temp [cn to] = [connections] [cn from]

 

83

AND CHARINDEX(CONCAT('[', [connections] [cn to], ']'),

 

84

 

#connections temp [cn route] )

= 0

85) AS [connections next]

86LEFT JOIN #connections temp

87

ON [connections next] [cn

from] =

#connections temp [cn from]

88

AND [connections next]

[cn to]

= #connections temp [cn to]

89WHERE #connections temp [cn from] IS NULL

90AND #connections_temp [cn_to] IS NULL;

92SET @rows inserted = @@ROWCOUNT;

93 END; — WHILE

94

95— Извлечение маршрутов, соответствующих условию поиска:

96SELECT *

97FROM #connections temp

98WHERE [cn from] = @start node

99AND [cn to] = @finish node

100ORDER BY [cn cost] ASC;

101GO

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

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

Для второго варианта решения, как и в случае с MySQL, нам понадобятся вспомогательные таблицы для хранения текущего и финальных путей.

MS S

QL I

Решение 7.1.2.b (подготовка ко второму варианту решения)

|

1-- Создание таблицы для хранения текущего пути:

2IF OBJECT_ID('tempdb.dbo.#current_path', 'U') IS NOT NULL

3DROP TABLE #current_path

4CREATE TABLE #current_path

5(

6 [cp id] INT NOT NULL IDENTITY 1 1 ,

7[cp from] INT,

8[cp to] INT,

9[cp cost] DOUBLE PRECISION,

10[cp bidir] CHAR 1

11);

12

13-- Создание таблицы для хранения готовых путей:

14IF OBJECT ID('tempdb.dbo.#final_paths', 'U') IS NOT NULL

15DROP TABLE #final_paths;

16CREATE TABLE #final_paths

17(

18[fp id] DOUBLE PRECISION,

19[fp from] INT,

20[fp to] INT,

21[fp cost] DOUBLE PRECISION,

22[fp bidir] CHAR 1

23);

Алгоритм второго варианта решения:

если текущий путь пуст, отправной точкой является точка старта, иначе отправной точкой является точка прибытия последней связи в пути (строки 3445);

открыть курсор для выбора всех связей между городами (строки 18-32, 47);

для всех связей повторять цикл, в котором:

опроверить, совпадает ли отправная точка рассматриваемой связи с текущей отправной точкой (строки 60-61 и, если нет, перейти к следующей итерации цикла;

опроверить, не присутствует ли уже рассматриваемая связь в пути (строки 63-67) и не приводит ли переход по этой связи к циклическому маршруту (строки 70-73) — в случае выполнения любого из этих условий перейти к следующей итерации цикла;

опроверить (строка 76), не совпала ли конечная точка связи с точкой финиша:

если совпала — мы нашли путь, для которого генерируем уникальный идентификатор (строка 78) и переносим в таблицу для хранения найденных путей (строки 79-90), не забыв добавить в конец саму связь, которую мы только что рассматривали (строки

91-101);

если не совпала — путь ещё не найден, а потому: добавляем рассматриваемую связь к текущему пути (строки 106-114), выполняем рекурсивный вызов (строка 117), после которого убираем из текущего пути последнюю связь (строки 120-121).

По завершении работы в таблице final_paths будут находиться все найденные пути между двумя указанными городами.

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

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

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

1

CREATE PROCEDURE FIND PATH

2

@start node INT,

3

@finish node INT

4AS

5-- Переменные для извлечения данных из курсора:

6DECLARE @cn from value INT = 0;

7DECLARE @cn to value INT = 0;

8DECLARE @cn cost value DOUBLE PRECISION = 0;

9DECLARE @cn_bidir_value CHAR(1 = '0';

10

11-- Текущая "отправная точка"

12DECLARE @from_node INT = 0;

14-- Идентификатор найденного пути

15DECLARE @rand_value DOUBLE PRECISION = 0 ;

17-- Курсор для прохода по связям между городами

18DECLARE nodes cursor CURSOR LOCAL FAST FORWARD FOR

19SELECT *

20

FROM (SELECT [cn from] ,

21

 

[cn to],

22

 

[cn

cost],

23

 

[cn

bidir]

24

FROM

[connections]

25UNION

26SELECT [cn to],

27

 

[cn from] ,

28

 

[cn

cost],

29

 

[cn

bidir]

30

FROM

[connections]

31WHERE [cn bidir] = 'Y'

32) AS [connections_bidir];

34IF ((SELECT COUNT(1)

35FROM #current_path) = 0

36-- Если текущий путь пуст, отправной точкой является точка старта

37SET @from node = @start node

38ELSE

39-- Если текущий путь НЕ пуст, отправной точкой

40-- является точка прибытия последней связи в пути

41SET @from node = (SELECT [cp to]

42

FROM

#current_path

43

WHERE

[cp id] = (SELECT MAX([cp id]

44

 

FROM #current_path

45

 

);

46

 

 

47

OPEN nodes_cursor

 

48

 

 

49WHILE (1 = 1)

50BEGIN

51FETCH NEXT FROM nodes cursor INTO @cn from value,

52

@cn to value

53

@cn

cost value,

54

@cn

bidir value;

55IF (@@FETCH STATUS != 0

56BREAK;

57

58-- Отправная точка связи не совпадает с текущей

59-- отправной точкой, пропускаем

60IF (@cn from value != @from node

61CONTINUE;

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

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

MS SQL

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

і

62- Такая связь уже есть в текущем пути, пропускаем

63IF EXISTS (SELECT 1

64

FROM #current_path

 

WHERE

[cp_from] =

@cn_from_value

65

AND

[cp_to] = @cn_to_value

66

CONTINUE;

 

 

67

 

 

 

 

 

68

— Такая связь приводит к циклу, пропускаем

69

IF EXISTS (SELECT 1

 

70

 

FROM #current_path

 

71

 

WHERE

[cp_from] =

@cn_to_value)

72

CONTINUE;

 

 

73

 

 

 

 

 

74

— Конечная точка связи совпала с точкой финиша, путь найден

75

IF (@cn_to_value = @finish_node

76

BEGIN

77

SET @rand_value = RAND();

78

INSERT INTO #final_paths

79

([fp_id],

80

[fp_from],

81

[fp_to],

82

[fp_cost],

83

[fp_bidir])

84

SELECT @rand_value,

85

[cp_from],

86

[cp_to],

87

[cp_cost],

88

[cp_bidir]

 

89FROM #current_path;

90INSERT INTO #final_paths

92

([fp_id],

[fp_from],

93

[fp_to],

94

[fp_cost],

95

[fp_bidir])

96

VALUES (@rand_value,

97

@ cn_f rom_value

98

@cn_to_value,

99

@ cn_cost_value

100

@cn_bidir_value);

101

END ELSE

102

BEGIN

103

— Добавляем связь в текущий путь

104

INSERT INTO #current_path

105

[cp_from],

106

[cp_to],

107

[cp_cost],

108

[cp_bidir])

109

VALUES @cn_from_value,

110

@ cn_to_value

111

@ cn_cos t_value, @cn_bidir_value);

112

 

113

-- Продолжаем рекурсивно искать следующие связи EXEC FIND_PATH

114

@start_node @finish_node;

115

 

116-- Удаляем последнюю связь из текущего пути

117DELETE FROM #current_path

118

WHERE [cp_id] = (SELECT MAX([cp_id]) FROM #current_path);

119END;

120END;

121CLOSE nodes_cursor

122DEALLOCATE nodes_cursor;

123GO

124

125

126

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

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

Проверим, как работают полученные решения, выполнив такой код.

MS SQL і

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

[

1-- Для первого варианта решения:

2EXEC FIND_PATH {начальная_точка}, {конечная_точка};

4-- Для второго варианта решения:

5TRUNCATE TABLE#current_path

6TRUNCATE TABLE#final_paths 1

EXEC FIND_PATH {начальная_точка}, {конечная_точка};

8SELECT * FROM #final paths;

Поведение MS SQL Server оказывается полностью эквивалентным поведе-

нию MySQL.

На представленном в начале данного примера наборе данных для поиска пути из города 1 в город 6 оба решения возвращают одинаковые (хоть и по-разному представленные) результаты.

Результат первого варианта решения:

cn_from

cn_to

cn_cost

cn_bidir

cn_steps

cn route

1

6

30

N

3

[1][7][3][6]

1

6

85

N

3

[1][7][2][6]

Результат второго варианта решения:

fp_id

fp_from

fp_to

fp_cost

fp_bidir

0.159113517642648

1

7

20

N

0.159113517642648

7

2

15

Y

0.159113517642648

2

6

50

N

0.716279602109358

1

7

20

N

0.716279602109358

7

3

5

N

0.716279602109358

3

6

5

N

 

Но если в таблицу connections поместить следующие данные

 

 

 

 

 

 

 

 

 

 

 

 

cn_from

cn_to

cn_cost

cn_bidir

 

 

 

 

 

1

 

3

100

N

 

 

 

 

 

 

 

1

 

5

100

N

 

 

 

 

 

 

 

3

 

5

20

 

N

 

 

 

 

 

 

 

5

 

3

200

N

 

 

 

 

 

 

 

и поискать путь между городами 1 и 5, результаты будут разными.

 

Результат первого варианта решения:

 

 

 

 

cn_from

cn_to

cn_cost

cn_bidir

cn_steps

 

cn route

 

1

 

5

100

N

 

 

1

 

[1][5]

 

 

 

Результат второго варианта решения:

 

 

 

 

 

fp_id

 

fp_from

fp_to

fp_cost

fp_bidir

 

0.948062188221448

1

 

3

 

100

N

 

 

0.948062188221448

3

 

5

 

20

N

 

 

0.562740117282937

1

 

5

 

100

N

 

 

Таким образом, как и было сказано выше, в MS SQL Server первый вариант решения тоже не находит альтернативные пути разной длины, в то время как второй вариант справляется с этим.

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

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

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

Переходим к Oracle и реализуем решение, представленное выше для MySQL

иMS SQL Server.

Вотличие от двух других СУБД, Oracle не позволяет скомпилировать хранимую процедуру, внутри которой есть обращение к несуществующим объектам — даже если объекты создаются в этой же процедуре несколькими строками выше. Это ограничение можно обходить через EXECUTE IMMEDIATE и иными изощрёнными способами, но для простоты кода мы вынесем создание временной таблицы, с которой работает хранимая процедура, в отдельный код.

Oracle

і

Решение 7.1.2.b (подготовка к первому варианту решения)

і

1CREATE GLOBAL TEMPORARY TABLE "connections temp"

2(

3"cn from" NUMBER(10),

4"cn to" NUMBER 10),

5"cn cost" DOUBLE PRECISION,

6"cn bidir" CHAR 1 ,

7"cn steps" NUMBER 5),

8"cn route" VARCHAR 1000

9)

10ON COMMIT PRESERVE ROWS;

Алгоритм первого варианта решения:

в созданную до компиляции хранимой процедуры временную таблицу переносятся все данные из таблицы connections с учётом двунаправленности некоторых связей (строки 10-28; аналогичный подзапрос, учитывающий двунаправленные связи, используется в строках 55-67 — фактически, он представляет собой ничто иное, как тело представления из решения{492} задачи

7.1.2.a{491});

выполняется цикл поиска производных маршрутов (строки 32-79), в котором:

оусловием выхода является отсутствие новых маршрутов (переменная

Oracle SQL%ROWCOUNT содержит количество записей, затронутых последней операцией модификации данных);

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

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

Обратите внимание на то, как формируется и анализируется маршрут, раз-

мещаемый в поле cn_route таблицы connections_temp: в Oracle (как и в MS SQL Server) нет прямого аналога функции MySQL FIND_IN_SET, а при поиске

вхождения подстроки в строку есть шанс, например, «найти» число 12 в числе 123 и т.д.

Потому каждое значение идентификатора берётся в квадратные скобки (маршрут примет вид наподобие «[1][7][3][6]»), и поиск тоже производится с предварительным заключением искомого идентификатора в квадратные скобки, что гарантирует отсутствие ложных срабатываний.

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