Opisgraf skierowany w Oracle SQL przy użyciu kwerendy rekurencyjnej odwiedzenie każdego węzła tylko raz
W naszej domenie problemu pracujemy nad zestawem krawędzi, które łączyć ze sobą tworząc wykres. Począwszy od danego węzła (lub węzłów), musimy wyświetlić wszystkie łącza na całym wykresie, które są podłączone do danego węzła (lub węzłów). Musimy pokazać te linki od lewej do prawej, od góry do dołu.
Mamy działającą kwerendę dla tego problemu dla wykresów z ograniczoną liczbą pętli. Większa liczba pętli wydłuża czas wykonania wykładniczo.
Musimy ograniczyć odwiedziny do tego samego węzła podczas rekursji, aby uzyskać działające rozwiązanie.
Poniższy przykład zawiera tylko jedną pętlę, ale ta pojedyncza pętla powoduje już 86 dodatkowych i nieaktualnych wierszy.
W podobnych postach dostarczane są rozwiązania dla PostgreSQL przy użyciu ROW i DOWOLNYCH operatorów, ale nie mogłem znaleźć rozwiązania Oracle.
Poszukujemy alternatywy dla naszego rozwiązania lub sposobu ograniczenia liczby odwiedzin na tych samych krawędziach.
Każda pomoc jest bardzo doceniana!
podobne
Visiting a directed graph as if it were an undirected one, using a recursive query zapewnia rozwiązanie PostgreSQL. Musimy używać Oracle11g.
Przykład
Krawędzie
A-B, B-D, C-A, C-E, C-F, H-F, E-B, G-D, G-I
graficzny
A
/ \
C - E - B - D
\ /
H - F G - I
DLL i DML
CREATE TABLE EDGE (
FROM_ID VARCHAR(10),
TO_ID VARCHAR(10)
);
INSERT INTO EDGE VALUES ('A', 'B');
INSERT INTO EDGE VALUES ('E', 'B');
INSERT INTO EDGE VALUES ('C', 'E');
INSERT INTO EDGE VALUES ('C', 'A');
INSERT INTO EDGE VALUES ('C', 'F');
INSERT INTO EDGE VALUES ('B', 'D');
INSERT INTO EDGE VALUES ('G', 'D');
INSERT INTO EDGE VALUES ('H', 'F');
INSERT INTO EDGE VALUES ('G', 'I');
Wejście
nodes: 'A'
wymaganej wydajności
C A
C E
C F
H F
A B
E B
B D
G D
G I
obecne rozwiązanie
Nasze obecne rozwiązanie zwraca dokładnie to, czego potrzebujemy, ale jak wspomniałem bove każda dodatkowa pętla wydłuża czas wykonania wykładniczo.
SELECT
c.LVL,
c.FROM_ID,
c.TO_ID,
CASE
WHEN lag(C.TO_ID)
OVER (
PARTITION BY C.LVL
ORDER BY C.LVL, C.TO_ID) = C.TO_ID
THEN C.LVL || '-' || C.TO_ID
WHEN lead(C.TO_ID)
OVER (
PARTITION BY C.LVL
ORDER BY C.LVL, C.TO_ID) = C.TO_ID
THEN C.LVL || '-' || C.TO_ID
ELSE C.LVL || '-' || C.FROM_ID
END GROUP_ID
FROM (
WITH chain(LVL, FROM_ID, TO_ID) AS (
SELECT
1 LVL,
root.FROM_ID FROM_ID,
root.TO_ID TO_ID
FROM EDGE root
WHERE root.TO_ID IN (:nodes)
OR (root.FROM_ID IN (:nodes) AND NOT EXISTS(
SELECT *
FROM EDGE
WHERE TO_ID IN (:nodes)
))
UNION ALL
SELECT
LVL +
CASE
WHEN previous.TO_ID = the_next.FROM_ID
THEN 1
WHEN previous.TO_ID = the_next.TO_ID
THEN 0
WHEN previous.FROM_ID = the_next.FROM_ID
THEN 0
ELSE -1
END LVL,
the_next.FROM_ID FROM_ID,
the_next.TO_ID TO_ID
FROM EDGE the_next
JOIN chain previous ON previous.TO_ID = the_next.FROM_ID
OR the_next.TO_ID = previous.FROM_ID
OR (previous.TO_ID = the_next.TO_ID AND previous.FROM_ID <> the_next.FROM_ID)
OR (previous.TO_ID <> the_next.TO_ID AND previous.FROM_ID = the_next.FROM_ID)
)
SEARCH BREADTH FIRST BY FROM_ID SET ORDER_ID
CYCLE FROM_ID, TO_ID SET CYCLE TO 1 DEFAULT 0
SELECT
C.*,
row_number()
OVER (
PARTITION BY LVL, FROM_ID, TO_ID
ORDER BY ORDER_ID) rank
FROM chain C
ORDER BY LVL, FROM_ID, TO_ID
) C
WHERE C.rank = 1;
Zamawianie oczekiwanego wyniku nie ma dla mnie sensu, przepraszam. Czy mógłbyś opisać krok po kroku przebieg swojego wykresu w odniesieniu do kolejności oczekiwanych wyników? – nop77svk
Chodzi o to, aby grupować elementy razem z tym samym rodzicem. Jeśli nie znaleziono wspólnego rodzica, elementy są pogrupowane, jeśli mają wspólne dzieci. We wszystkich innych przypadkach stosuje się porządek naturalny. Wprowadziłem pojęcie LVL, aby uzyskać informację o poziomej odległości od punktu początkowego. Węzły ze wspólnym rodzicem lub ze wspólnym dzieckiem powinny mieć tę samą głębokość. Zmniejszę LVL, jeśli następny element został przyłączony przy użyciu jego kolumny TO_ID. Zwiększam LVL, jeśli następny element został połączony przy użyciu jego kolumny FROM_ID. We wszystkich innych przypadkach LVL pozostaje ten sam. –
Ale samo zamówienie jest mniej ważne. Szukam sposobu na ograniczenie eksplozji ścieżek podczas rekursji. Uważam, że rozwiązanie z https://stackoverflow.com/questions/8764701/visiting-a-directed-graph-as-if-it-were-an-undirected-one-using-a-recursive-que is the way iść, ale potrzebuję odpowiednika Oracle. Próbowałem użyć ciąg znaków reprezentujących ścieżkę, ale potem otrzymam _result ciąg łączenia ciągów jest zbyt long_. –