Oracle /过程在两个节点之间查找路径

时间:2018-01-24 14:58:00

标签: oracle plsql

我还是oracle的新手,我需要以下方面的帮助:

让我们说我有一个包含节点的表:

DECLARE @nodes TABLE (node VARCHAR(5))
INSERT INTO @nodes 
VALUES
('A'),
('B'),
('C'),
('D')

以及包含这些节点如何连接在一起的表格:

DECLARE @connected_nodes TABLE (node_1 VARCHAR(5),node_2 VARCHAR(5))
INSERT INTO @connected_nodes 
VALUES
('A','B'),
('B','C'),
('A','C'),
('C','D')

我需要在oracle中编写一个程序,找到两个节点之间的路径,这意味着,例如,如果我想从A到C,我在上面的例子中有两个路径: A - &以及c A - B - &以及c 所以程序必须返回这两个路径以及跳数(在这种情况下,第一个路径为1,第二个路径为2)

注意到存在数百/数千个节点。

非常感谢您的帮助

3 个答案:

答案 0 :(得分:1)

请注意,您不能像在SQL SERVER中那样在Oracle中使用DECLARE @connected_nodes TABLE。您需要创建一个表并将记录插入表中。

使用此分层查询可以实现您需要显示的输出。

select LTRIM ( SYS_CONNECT_BY_PATH ( node_1,'->' ) ,'->')as paths 
, LEVEL-1 as number_of_hops
FROM connected_nodes WHERE LEVEL > 1 
CONNECT  BY NOCYCLE PRIOR node_2 = node_1
;

以下是所有步骤的完整演示。

DEMO

编辑::如果您只需要查找特定节点之间的路径(A-> C),请使用 附加条件。

select LTRIM ( SYS_CONNECT_BY_PATH ( node_1,'->' ) ,'->')as paths 
, LEVEL-1 as number_of_hops
FROM connected_nodes WHERE
node_1 = 'C' 
START WITH node_1 = 'A' 
CONNECT  BY NOCYCLE PRIOR node_2 = node_1
;

答案 1 :(得分:1)

您需要一个分层查询来获得所需的结果,应用一些逻辑来仅获取从给定值到另一个值的路径:

with nodes (node) as (
    select 'A' from dual union all
    select 'B' from dual union all
    select 'C' from dual union all
    select 'D' from dual
),
connected_nodes (node_1,node_2 ) as
(
    select 'A','B' from dual union all
    select 'B','C' from dual union all
    select 'B','D' from dual union all
    select 'A','C' from dual union all
    select 'A','D' from dual union all
    select 'D','C' from dual union all
    select 'C','D' from dual
)
select ltrim(sys_connect_by_path(node_1, '->'), '->') as path,
       level -1 as hops
from
    (
    select node as node_1, node_2
    from nodes
           left join connected_nodes
             on(node = node_1)
    )         
where node_1 = 'C'               /* where to stop */
connect by nocycle prior node_2 = node_1
               and prior node_1 is not null
start with node_1 = 'A'         /* where to start from */;

给出:

PATH             HOPS
---------- ----------
A->B->C             2
A->B->D->C          3
A->C                1
A->D->C             2

答案 2 :(得分:0)

这可能是一种没有分层查询的方法。 他们让我头疼。

drop table connected_nodes;

create table connected_nodes (node_1 VARCHAR2(5),node_2 VARCHAR2(5));

INSERT INTO connected_nodes VALUES('A','B');
INSERT INTO connected_nodes VALUES('B','A');
INSERT INTO connected_nodes VALUES('B','C');
INSERT INTO connected_nodes VALUES('A','C');
INSERT INTO connected_nodes VALUES('C','D');

commit;

drop table links;

create table links 
(from_node_1 VARCHAR2(5),
 from_node_2 VARCHAR2(5),
 to_node_1 VARCHAR2(5),
 to_node_2 VARCHAR2(5),
 first_node_1 VARCHAR2(5),
 first_node_2 VARCHAR2(5),
 link_num NUMBER);

drop table paths;

create table paths as select * from links;

create or replace procedure get_paths(p_node_1 VARCHAR2,p_node_2 VARCHAR2)
as
prev_num_links number;
num_links number;
begin

-- get first links in paths

insert into links 
select 
NULL,
NULL,
cn.node_1,
cn.node_2,
cn.node_1,
cn.node_2,
1
from 
connected_nodes cn
where 
cn.node_1 = p_node_1;

-- loop until number of path links does not increase

prev_num_links := 0;

loop

select count(*) into num_links from links;

if num_links = prev_num_links then
    exit;
end if;

-- add new links

insert into links 
select 
l.to_node_1,
l.to_node_2,
c.node_1,
c.node_2,
l.first_node_1,
l.first_node_2,
l.link_num+1
from connected_nodes c,links l
where 
l.to_node_2 = c.node_1 and
l.to_node_2 <> p_node_2 and
(l.to_node_1,
l.to_node_2,
c.node_1,
c.node_2) not in
(select 
from_node_1,
from_node_2,
to_node_1,
to_node_2
from links);

commit;

prev_num_links := num_links;

end loop;

-- populate paths table with links that go backward
-- from end node to beginning.

-- add end nodes

insert into paths
select * from links
where to_node_2 = p_node_2;

-- loop until number of paths rows does not increase

prev_num_links := 0;

loop

select count(*) into num_links from paths;

if num_links = prev_num_links then
    exit;
end if;

-- add new links

insert into paths 
select 
*
from links l
where 
(l.to_node_1,
l.to_node_2,
l.first_node_1,
l.first_node_2,
l.link_num+1)
in
(select
from_node_1,
from_node_2,
first_node_1,
first_node_2,
link_num
from paths) and
(l.from_node_1,
l.from_node_2,
l.to_node_1,
l.to_node_2,
l.first_node_1,
l.first_node_2,
l.link_num)
not in
(select * from paths);

commit;

prev_num_links := num_links;

end loop;

end;
/
show errors

execute get_paths('A','C');

select 
to_node_1,to_node_2,link_num,first_node_1,first_node_2
from paths
order by first_node_1,first_node_2,link_num;

输出如下:

TO_NO TO_NO   LINK_NUM FIRST FIRST
----- ----- ---------- ----- -----
A     B              1 A     B
B     C              2 A     B
A     C              1 A     C

列first_node_1和first_node_2 定义路径和link_num列 是它在路径中的链接。

超长而且凌乱。可能是我没有处理的案件。 我想除非你不能使用分层查询。这是一 尝试在没有它们的情况下使用SQL和PL / SQL。