使用Oracle 11g,我试图在ID对的无向图中找到最小的ID。
使用以下对:
create table unique_pairs ( ID1 INT, ID2 INT );
insert all
into unique_pairs (ID1,ID2) VALUES ( 1, 2 )
into unique_pairs (ID1,ID2) VALUES ( 1, 3 )
into unique_pairs (ID1,ID2) VALUES ( 4, 2 )
into unique_pairs (ID1,ID2) VALUES ( 4, 5 )
into unique_pairs (ID1,ID2) VALUES ( 7, 6 )
into unique_pairs (ID1,ID2) VALUES ( 8, 10 )
into unique_pairs (ID1,ID2) VALUES ( 10, 7 )
select * from dual;
commit;
我正在使用两个通过查询连接的联合来尝试在地图的两个方向上旅行:
select distinct a.ID1, a.ID2, min(a.ultimate_parent_id ) as ultimate_parent_id
from
(SELECT distinct ID1, ID2,
connect_by_root(ID2) ultimate_parent_id
FROM unique_pairs
CONNECT BY ID2 = prior ID1 AND ID2 != PRIOR ID2
union
SELECT distinct ID1, ID2,
connect_by_root(ID1) ultimate_parent_id
FROM unique_pairs
CONNECT BY ID1 = prior ID2 AND ID1 != PRIOR ID1) a
group by a.ID1, a.ID2
order by 3;
我明白了:
ID1 ID2 ULTIMATE_PARENT_ID
1 2 1
1 3 1
4 2 2
4 5 4
10 7 6
7 6 6
8 10 6
应该是这样的:
ID1 ID2 ULTIMATE_PARENT_ID
1 2 1
1 3 1
4 2 1
4 5 1
10 7 6
7 6 6
8 10 6
我认为我的问题涉及当我的数据是非定向时,按功能连接意味着层次结构。任何帮助都会很棒,谢谢!
答案 0 :(得分:2)
Collapsar有一个非常冗长的描述,但这是一个非常简短的解决方案,符合您的要求。我离图形理论的日子太过分了,不像他那样解释它,但简单的解释是,由于你的unique_pairs代表无向图中的边,它可以在有向图中表示为ID1的并集, ID2与ID2,ID1因此是我的第一个公用表表达式。使用该有向图,您可以像我的第二个公用表表达式一样计算所有连接的节点对。最后,您可以将原始unique_pairs表连接到ID1上的已连接对CTE,并将最小根节点作为ULTIMATE_PARENT_ID:
with dg1 as (
select id1, id2 from unique_pairs
union all
select id2, id1 from unique_pairs
), cp as (
select id1
, CONNECT_BY_ROOT id1 root
from dg1
connect by nocycle prior id1 = id2
)
select dg1.*
, min(root) ULTIMATE_PARENT_ID
from unique_pairs dg1
join cp
on cp.id1 = dg1.id1
group by dg1.id1, dg1.id2
order by 1,2
请参阅SQL Fiddle
| ID1 | ID2 | ULTIMATE_PARENT_ID |
|-----|-----|--------------------|
| 1 | 2 | 1 |
| 1 | 3 | 1 |
| 4 | 2 | 1 |
| 4 | 5 | 1 |
| 7 | 6 | 6 |
| 8 | 10 | 6 |
| 10 | 7 | 6 |
答案 1 :(得分:1)
<强>概念强>
您的问题基本上由3个步骤组成:
G
任务通过以下方式解决:
D_1
顶点id的自然数字排序的有向图G
。D_1
所代表的排序中的链来将B
缩减为包含所有候选ID的二分图D_1
。B
中的最小代表ID。G
的边缘到B
边缘的映射,从而建立其弱连接组件(WCC)。<强>详情
G
的边集恰好是(id1, id2)
记录给出的对unique_pairs
的集合。 G
的顶点集与id
的{{1}}列unique_pairs
列的值集匹配。由于G
是无向的,因此是否通过记录(id1, id2)
或(id2, id1)
表示边缘并不重要;从此我们假设id1 < id2
为(id1, id2)
中的每条记录unique_pairs
保留了wlog。
我们通过根据从D_1
导出的有向图G
构建辅助结构,通过根据其入射顶点的id的数字顺序定向无向图的每个边来解决任务。让D_2
为D_1
,使所有边缘方向反转。识别原始图表的连接组件相当于在D_1
或D_2
中识别WCC。通过构造观察D_1
是非循环的。
辅助辅助结构是G
中的一组边缘映射到D_1
中最大链的幂集(=未包含在另一个路径中的路径),以便所有链中的边e
的图像包含e
并最大化其终端的数字id(=开始和结束顶点)之间的差异。 powersets的使用是技术性的,否则当两个最大链共享相同的终端但遵循“替代路由”时,映射可能不是很明确。 (通常图像将包含单个元素)。
在下一步中,我们粗化映射:代替最大链组,图像将是原始映射图像中任何最大链的终端ID对。
鉴于此映射,我们构建了辅助二分图B
,其边缘精确地表示有序的终端ID对。该图是二分的,因为最大链的末端顶点不能是另一个最大链的起始顶点,反之亦然 - 如果发生这种情况,则所述链不会是最大的。
但是,通过构造,B
构成了D_1
传递闭包的一部分。因此,B
的WCC的顶点集是D_1
的WCC的顶点集的子集。我们最终对后面每个WCC中具有最小id的顶点感兴趣。由于最大链中包含的终端id不能小于起始顶点id,因此所有这些顶点必须包含在B
的顶点集中。因此,我们必须考虑B
的WCC,只考虑每个WCC的最小终端ID。连同所有G
边缘到B
边缘的辅助映射,以及B
的WCC,我们解决了原始问题。
SQL实施
在预处理步骤中,G
的边缘被归一化为其入射顶点的自然排序。假设边的方向为id1 -> id2
,test_unique_pairs
也代表D_1
。
update test_unique_pairs
set id1 = id2
, id2 = id1
where id1 > id2
;
第一个查询将D_1
的边缘映射到B
的边缘,即。 D_1
中最大链的终端id对。这些链是非构造的非循环,将通过test_unique_pairs
上的分层查询计算,它们将由它们的起点和终点顶点表示,这些对也表示B
中的边。这种映射以共模两个终端的模数替代链发生 - 如果它们共享两个终端,则认为不同的最大路径是等效的。 oracle中的分层查询允许轻松提取最大元素。然而,最小元素是层次结构的最大元素,其中父子关系被反转(为了满足这种计算需要考虑两个有向图D_1
,D_2
)。
为了简化第二个处理阶段,查询ID包含在视图定义中:
create or replace view test_up_chains as
select *
from ( -- collect minimal/maximal reachable id for each edge in G
select distinct
inter.id1
, inter.id2
, min(minelt) chainstart
, max(maxelt) chainend
from ( -- collect minimum/maximum terminal id for chains starting/ending with any of G's edges
select distinct
inter_min.id1
, inter_min.id2
, inter_min.minelt
, inter_max.maxelt
from ( -- hierarchical query for maximal chains in D_1
select distinct
hup.ID1
, hup.ID2
, CONNECT_BY_ROOT hup.id2 maxelt
from test_unique_pairs hup
connect by nocycle prior hup.id1 = hup.id2
start with hup.id2 in (
select ref1.id2
from test_unique_pairs ref1
where not exists (
select 1
from test_unique_pairs ref2
where ref2.id1 = ref1.id2
)
)
) inter_max
join ( -- hierarchical query for maximal chains in D_2 ( = D_1 with reversed edge orientations )
select distinct
hup.ID1
, hup.ID2
, CONNECT_BY_ROOT hup.id1 minelt
from test_unique_pairs hup
connect by nocycle prior hup.id2 = hup.id1
start with hup.id1 in (
select ref1.id1
from test_unique_pairs ref1
where not exists (
select 1
from test_unique_pairs ref2
where ref2.id2 = ref1.id1
)
)
) inter_min
on ( inter_min.id1 = inter_max.id1 AND inter_min.id2 = inter_max.id2 )
) inter
group by grouping sets ( (inter.id1, inter.id2) )
) base
order by base.id1
, base.id2
;
接下来,B
的边缘被合并到WCC中,并提取它们各自的最小id。以下分层查询(缺少STARTS WITH
子句)构建了边缘入射关系的完整传递闭包,并且B
中的每个边都生成到同一WCC中所有其他边的根路径。因此,在G
中给定边缘的所有层次结构的起始顶点上的最小化找到G
中的最小可达ID。循环由NOCYCLE
指令抑制。
select *
from (
select distinct
id1
, id2
, min(chainlim) chainrep
from (
select distinct
id1
, id2
, connect_by_root chainstart chainlim
from test_up_chains
connect by nocycle ( ( prior chainstart = chainstart and prior chainend != chainend ) OR ( prior chainstart != chainstart and prior chainend = chainend ) )
) base
group by grouping sets ( ( base.id1, base.id2 ) )
)
order by id1
, id2
;