Пример 47: формирование и анализ связанных структур
|
Oracle |
Решение 7.1.2.b (код процедуры, первый вариант) (продолжение) |
|||
|
55 |
|
|
JOIN (SELECT |
"cn_from", |
|
56 |
|
|
|
"cn_to", |
|
57 |
|
|
|
"cn_cost", |
|
58 |
|
|
|
"cn_bidir" |
|
59 |
|
|
FROM |
"connections" |
|
60 |
|
|
|
UNION |
|
61 |
|
|
SELECT |
"cn_to", |
|
62 |
|
|
|
"cn_from", |
|
63 |
|
|
|
"cn_cost", |
|
64 |
|
|
|
"cn_bidir" |
|
65 |
|
|
FROM |
"connections" |
|
66 |
|
|
WHERE |
"cn_bidir" = 'Y' |
|
67 |
|
|
) "connections" |
|
|
68 |
|
|
ON "connections_temp"."cn_to" = "connections"."cn_from" |
|
|
69 |
|
|
AND INSTR("connections_temp"."cn_route", |
|
|
70 |
|
|
|
'[' || "connections"."cn_to" || ']') = 0 |
71) "connections_next"
72LEFT JOIN "connections_temp"
73 |
|
ON "connections_next"."cn_from" = |
"connections_temp"."cn_from" |
74 |
|
AND "connections_next"."cn_to" |
= "connections_temp"."cn_to" |
75WHERE "connections_temp"."cn_from" IS NULL
76AND "connections_temp"."cn_to" IS NULL;
77
78rows_inserted := SQL%ROWCOUNT;
79END LOOP;
80
81-- Извлечение маршрутов, соответствующих условию поиска:
82OPEN final_paths FOR
83SELECT *
84FROM "connections_temp"
85WHERE "cn_from" = start_node
86AND "cn_to" = finish_node
87ORDER BY "cn_cost" ASC;
88END;
Для второго варианта решения, как и в случае с MySQL и MS SQL Server, нам понадобятся вспомогательные таблицы для хранения текущего и финальных путей.
Oracle |
Решение 7.1.2.b (подготовка ко второму варианту решения) |
1-- Создание таблицы для хранения текущего пути:
2CREATE GLOBAL TEMPORARY TABLE "current_path"
3(
4"cp_id" NUMBER(10),
5"cp_from" NUMBER(10),
6"cp_to" NUMBER(10),
7"cp_cost" NUMBER(15,4),
8"cp_bidir" CHAR(1)
9);
10
11-- Создание таблицы для хранения готовых путей:
12CREATE GLOBAL TEMPORARY TABLE "final_paths"
13(
14"fp_id" NUMBER(15,4),
15"fp_from" NUMBER(15,4),
16"fp_to" NUMBER(15,4),
17"fp_cost" NUMBER(15,4),
18"fp_bidir" CHAR(1)
19);
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 510/545
Пример 47: формирование и анализ связанных структур
Алгоритм второго варианта решения:
•если текущий путь пуст, отправной точкой является точка старта, иначе отправной точкой является точка прибытия последней связи в пути (строки 2640; обратите внимание на то, как в Oracle проверяется на пустоту результат выполнения запроса — классическое выражение IF (NOT) EXISTS здесь не работает);
•открыть курсор для выбора всех связей между городами (строки 8-24, 42);
•для всех связей повторять цикл, в котором:
oпроверить, совпадает ли отправная точка рассматриваемой связи с текущей отправной точкой (строки 46-49 и, если нет, перейти к следующей итерации цикла;
oпроверить, не присутствует ли уже рассматриваемая связь в пути (строки 52-60) и не приводит ли переход по этой связи к циклическому маршруту (строки 63-70) — в случае выполнения любого из этих условий перейти к следующей итерации цикла;
oпроверить (строка 73), не совпала ли конечная точка связи с точкой финиша:
если совпала — мы нашли путь, для которого генерируем уникальный идентификатор (строка 75) и переносим в таблицу для хранения найденных путей (строки 77-88), не забыв добавить в конец саму связь, которую мы только что рассматривали (строки
89-99);
если не совпала — путь ещё не найден, а потому: добавляем рассматриваемую связь к текущему пути (строки 102-113), выполняем рекурсивный вызов (строка 116), после которого убираем из текущего пути последнюю связь (строки 119-121).
По завершении работы в таблице final_paths будут находиться все
найденные пути между двумя указанными городами.
|
|
|
Обратите внимание, как в строках 108-109 реализована эмуляция автоинкре- |
||
ментируемого первичного ключа без использования триггера. |
|||||
|
|
|
|
|
|
|
Oracle |
|
Решение 7.1.2.b (код процедуры, второй вариант) |
||
|
1 |
|
CREATE OR REPLACE PROCEDURE FIND_PATH (start_node IN NUMBER, |
|
|
|
2 |
|
|
finish_node IN NUMBER) |
|
3AS
4from_node NUMBER(10) := 0;
5rows_count NUMBER(10) := 0;
6rand_value NUMBER(15,4) := 0;
7
8CURSOR nodes_cursor IS
9SELECT "cn_from",
10"cn_to",
11"cn_cost",
12"cn_bidir"
13FROM (SELECT "cn_from",
14 |
|
"cn_to", |
15 |
|
"cn_cost", |
16 |
|
"cn_bidir" |
17 |
|
FROM "connections" |
18UNION
19SELECT "cn_to",
20 |
|
|
"cn_from", |
21 |
|
|
"cn_cost", |
22 |
|
|
"cn_bidir" |
23 |
|
FROM |
"connections" |
24 |
|
WHERE |
"cn_bidir" = 'Y'); |
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 511/545
Пример 47: формирование и анализ связанных структур
Oracle Решение 7.1.2.b (код процедуры, второй вариант) (продолжение)
25BEGIN
26SELECT COUNT(1) INTO rows_count
27FROM "current_path";
28
29IF (rows_count = 0)
30THEN
31-- Если текущий путь пуст, отправной точкой является точка старта
32from_node := start_node;
33ELSE
34-- Если текущий путь НЕ пуст, отправной точкой
35-- является точка прибытия последней связи в пути
36SELECT "cp_to" INTO from_node
37FROM "current_path"
38WHERE "cp_id" = (SELECT MAX("cp_id")
39 |
|
FROM "current_path"); |
40 |
|
END IF; |
41 |
|
|
|
|
|
42FOR one_link IN nodes_cursor
43LOOP
44-- Отправная точка связи не совпадает с текущей
45-- отправной точкой, пропускаем
46IF (one_link."cn_from" != from_node)
47THEN
48CONTINUE;
49END IF;
50
51-- Такая связь уже есть в текущем пути, пропускаем
52SELECT COUNT(1) INTO rows_count
53FROM (SELECT 1
54FROM "current_path"
55WHERE "cp_from" = one_link."cn_from"
56AND "cp_to" = one_link."cn_to");
57IF (rows_count > 0)
58THEN
59CONTINUE;
60END IF;
61
62-- Такая связь приводит к циклу, пропускаем
63SELECT COUNT(1) INTO rows_count
64FROM (SELECT 1
65FROM "current_path"
66WHERE "cp_from" = one_link."cn_to");
67IF (rows_count > 0)
68THEN
69CONTINUE;
70END IF;
71
72-- Конечная точка связи совпала с точкой финиша, путь найден
73IF (one_link."cn_to" = finish_node)
74THEN
75rand_value := DBMS_RANDOM.VALUE(1,10);
76 |
|
|
|
77 |
|
INSERT |
INTO "final_paths" |
78 |
|
|
("fp_id", |
79 |
|
|
"fp_from", |
80 |
|
|
"fp_to", |
81 |
|
|
"fp_cost", |
82 |
|
|
"fp_bidir") |
83 |
|
SELECT |
rand_value, |
84 |
|
|
"cp_from", |
85 |
|
|
"cp_to", |
86 |
|
|
"cp_cost", |
87 |
|
|
"cp_bidir" |
88 |
|
FROM |
"current_path"; |
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 512/545
Пример 47: формирование и анализ связанных структур
|
Oracle |
Решение 7.1.2.b (код процедуры, второй вариант) (продолжение) |
||
|
89 |
|
|
INSERT INTO "final_paths" |
|
90 |
|
|
("fp_id", |
|
91 |
|
|
"fp_from", |
|
92 |
|
|
"fp_to", |
|
93 |
|
|
"fp_cost", |
|
94 |
|
|
"fp_bidir") |
|
95 |
|
|
VALUES (rand_value, |
|
96 |
|
|
one_link."cn_from", |
|
97 |
|
|
one_link."cn_to", |
|
98 |
|
|
one_link."cn_cost", |
|
99 |
|
|
one_link."cn_bidir"); |
|
|
|
|
|
100ELSE
101-- Добавляем связь в текущий путь
102INSERT INTO "current_path"
103 |
|
("cp_id", |
104 |
|
"cp_from", |
105 |
|
"cp_to", |
106 |
|
"cp_cost", |
107 |
|
"cp_bidir") |
108 |
|
VALUES (NVL((SELECT MAX("cp_id") + 1 |
109 |
|
FROM "current_path"), 1), |
110 |
|
one_link."cn_from", |
111 |
|
one_link."cn_to", |
112 |
|
one_link."cn_cost", |
113 |
|
one_link."cn_bidir"); |
114 |
|
|
115-- Продолжаем рекурсивно искать следующие связи
116FIND_PATH (start_node, finish_node);
117
118-- Удаляем последнюю связь из текущего пути
119DELETE FROM "current_path"
120WHERE "cp_id" = (SELECT MAX("cp_id")
121 |
|
FROM "current_path"); |
122END IF;
123END LOOP;
124END;
Проверим, как работают полученные решения, выполнив представленный ниже код. Поведение Oracle оказывается полностью эквивалентным поведению
MySQL и MS SQL Server.
На представленном в начале данного примера наборе данных для поиска пути из города 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 |
|
|
|||
4.5847 |
1 |
|
7 |
20 |
N |
|
|
|||
4.5847 |
7 |
|
2 |
15 |
Y |
|
|
|||
4.5847 |
2 |
|
6 |
50 |
N |
|
|
|||
8.5731 |
1 |
|
7 |
20 |
N |
|
|
|||
8.5731 |
7 |
|
3 |
5 |
|
N |
|
|
||
8.5731 |
3 |
|
6 |
5 |
|
N |
|
|
||
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 513/545
Пример 47: формирование и анализ связанных структур
Oracle |
Решение 7.1.2.b (код для проверки работоспособности) |
1-- Для первого варианта решения:
2TRUNCATE TABLE "connections_temp";
3DECLARE
4fp SYS_REFCURSOR;
5cn_from NUMBER(10);
6cn_to NUMBER(10);
7cn_cost DOUBLE PRECISION;
8cn_bidir CHAR(1);
9cn_steps NUMBER(5);
10cn_route VARCHAR(1000);
11BEGIN
12FIND_PATH({начальная_точка}, {конечная_точка}, fp);
13LOOP
14FETCH fp INTO cn_from,
15 |
|
cn_to, |
16 |
|
cn_cost, |
17 |
|
cn_bidir, |
18 |
|
cn_steps, |
19 |
|
cn_route; |
20EXIT WHEN fp%NOTFOUND;
21DBMS_OUTPUT.PUT_LINE(cn_from || ' | ' ||
22 |
|
cn_to || |
' | ' |
|| |
23 |
|
cn_cost || ' | |
' || |
|
24 |
|
cn_bidir |
|| ' | |
' || |
25 |
|
cn_steps |
|| ' | |
' || |
26 |
|
cn_route); |
|
|
27END LOOP;
28CLOSE fp;
29END;
30
31-- Для второго варианта решения:
32TRUNCATE TABLE "current_path";
33TRUNCATE TABLE "final_paths";
34BEGIN
35FIND_PATH({начальная_точка}, {конечная_точка});
36END;
37SELECT * FROM "final_paths";
Но если в таблицу 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 |
|
|
|||
3.5748 |
1 |
|
3 |
100 |
N |
|
|
|||
3.5748 |
3 |
|
5 |
20 |
N |
|
|
|||
5.881 |
1 |
|
5 |
100 |
N |
|
|
|||
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 514/545