有谁知道为什么Oracle在循环发生在顶节点(根节点连接回根节点)时继续遵循超出循环的路径?更重要的是,如何防止这种情况发生?
我使用Oracle 11g Release 2(11.2)并研究分层查询。我的问题将围绕Oracle数据库SQL语言参考手册页面9-4上图9-1中的树结构展开。
我使用供应商和客户的概念创建了这棵树的表结构:
create table t
( vendor varchar2(3)
, customer varchar2(3)
);
insert into t values ( '1' , '2' );
insert into t values ( '2' , '3' );
insert into t values ( '2' , '4' );
insert into t values ( '4' , '5' );
insert into t values ( '4' , '6' );
insert into t values ( '1' , '7' );
insert into t values ( '7' , '8' );
insert into t values ( '1' , '9' );
insert into t values ( '9' , '10' );
insert into t values ( '10' , '11' );
insert into t values ( '9' , '12' );
commit;
以下选择查询可以无障碍地遍历树:
select vendor,
customer,
level,
connect_by_isleaf as isleaf,
connect_by_iscycle as iscycle,
connect_by_root vendor||sys_connect_by_path(customer,' ~ ') as path
from t
connect by nocycle
vendor=prior customer
start with vendor='1';
给出结果:
Vendor Cust Level Isleaf Iscycle Path
1 2 1 0 0 1 ~ 2
2 3 2 1 0 1 ~ 2 ~ 3
2 4 2 0 0 1 ~ 2 ~ 4
4 5 3 1 0 1 ~ 2 ~ 4 ~ 5
4 6 3 1 0 1 ~ 2 ~ 4 ~ 6
1 7 1 0 0 1 ~ 7
7 8 2 1 0 1 ~ 7 ~ 8
1 9 1 0 0 1 ~ 9
9 10 2 0 0 1 ~ 9 ~ 10
10 11 3 1 0 1 ~ 9 ~ 10 ~ 11
9 12 2 1 0 1 ~ 9 ~ 12
我随后通过向结构中添加循环使事情变得更加复杂。首先是一个供应商的记录,他们向自己销售...
--self cycle
insert into t values ( '4' , '4' );
还有一个供应商的客户是他们供应商的情况...
(注:原文中的“whos”应为“whose”,我进行了修正。) --ancestor cycle
insert into t values ( '6' , '2' );
重新执行上述的选择查询会得到与上面相同的输出,只是第3行和第5行的Iscycle为1(路径1~2~4和1~2~4~6)。请注意,CONNECT BY命名法标记了循环的父记录而不是实际完成循环的子记录。(所以我知道4和6都回到了祖先,但我不知道是哪个祖先。)
添加两条记录会在原始树的分支之间创建一个更大的循环:
--cycle crossing branches of tree
insert into t values ( '6' , '9' );
insert into t values ( '11' , '2' );
重新执行选择查询会得到以下输出结果:
Vendor Customer Level Isleaf Iscycle Path
1 2 1 0 0 1 ~ 2
2 3 2 1 0 1 ~ 2 ~ 3
2 4 2 0 1 1 ~ 2 ~ 4
4 5 3 1 0 1 ~ 2 ~ 4 ~ 5
4 6 3 0 1 1 ~ 2 ~ 4 ~ 6
6 9 4 0 0 1 ~ 2 ~ 4 ~ 6 ~ 9
9 10 5 0 0 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10
10 11 6 1 1 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10 ~ 11
9 12 5 1 0 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 12
1 7 1 0 0 1 ~ 7
7 8 2 1 0 1 ~ 7 ~ 8
1 9 1 0 0 1 ~ 9
9 10 2 0 0 1 ~ 9 ~ 10
10 11 3 0 0 1 ~ 9 ~ 10 ~ 11
11 2 4 0 0 1 ~ 9 ~ 10 ~ 11 ~ 2
2 3 5 1 0 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 3
2 4 5 0 1 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4
4 5 6 1 0 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 5
4 6 6 1 1 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 6
9 12 2 1 0 1 ~ 9 ~ 12
输出结果与预期一致。当遇到循环时,所有周期都会被标记并停止映射。
现在是问题儿童...让我们向根节点添加一个自我循环,它与上面创建的第一个循环完全相同,只是针对节点1。
insert into t values ( '1' , '1' );
这次 Oracle 在节点1检测到了循环,符合预期(第一行的Iscycle标志被设置为1); 但是,它继续通过这个循环并构建整个树结构两次。第2至21行是第22至41行的重复,其中节点1的循环被添加到路径前面。
Vendor Customer Level Isleaf Iscycle Path
1 1 1 0 1 1 ~ 1
1 2 2 0 0 1 ~ 1 ~ 2
2 3 3 1 0 1 ~ 1 ~ 2 ~ 3
2 4 3 0 1 1 ~ 1 ~ 2 ~ 4
4 5 4 1 0 1 ~ 1 ~ 2 ~ 4 ~ 5
4 6 4 0 1 1 ~ 1 ~ 2 ~ 4 ~ 6
6 9 5 0 0 1 ~ 1 ~ 2 ~ 4 ~ 6 ~ 9
9 10 6 0 0 1 ~ 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10
10 11 7 1 1 1 ~ 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10 ~ 11
9 12 6 1 0 1 ~ 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 12
1 7 2 0 0 1 ~ 1 ~ 7
7 8 3 1 0 1 ~ 1 ~ 7 ~ 8
1 9 2 0 0 1 ~ 1 ~ 9
9 10 3 0 0 1 ~ 1 ~ 9 ~ 10
10 11 4 0 0 1 ~ 1 ~ 9 ~ 10 ~ 11
11 2 5 0 0 1 ~ 1 ~ 9 ~ 10 ~ 11 ~ 2
2 3 6 1 0 1 ~ 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 3
2 4 6 0 1 1 ~ 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4
4 5 7 1 0 1 ~ 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 5
4 6 7 1 1 1 ~ 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 6
9 12 3 1 0 1 ~ 1 ~ 9 ~ 12
1 2 1 0 0 1 ~ 2
2 3 2 1 0 1 ~ 2 ~ 3
2 4 2 0 1 1 ~ 2 ~ 4
4 5 3 1 0 1 ~ 2 ~ 4 ~ 5
4 6 3 0 1 1 ~ 2 ~ 4 ~ 6
6 9 4 0 0 1 ~ 2 ~ 4 ~ 6 ~ 9
9 10 5 0 0 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10
10 11 6 1 1 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10 ~ 11
9 12 5 1 0 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 12
1 7 1 0 0 1 ~ 7
7 8 2 1 0 1 ~ 7 ~ 8
1 9 1 0 0 1 ~ 9
9 10 2 0 0 1 ~ 9 ~ 10
10 11 3 0 0 1 ~ 9 ~ 10 ~ 11
11 2 4 0 0 1 ~ 9 ~ 10 ~ 11 ~ 2
2 3 5 1 0 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 3
2 4 5 0 1 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4
4 5 6 1 0 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 5
4 6 6 1 1 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 6
9 12 2 1 0 1 ~ 9 ~ 12
为什么1-1周期没有像4-4周期一样被处理?我错过了什么吗?
为了减轻这种情况,我在CONNECT BY子句中添加了一个额外的条件,要求客户不是“1”。
select vendor,
customer,
level,
connect_by_isleaf as isleaf,
connect_by_iscycle as iscycle,
connect_by_root vendor||sys_connect_by_path(customer,' ~ ') as path
from t
connect by nocycle
vendor=prior customer
and customer<>'1'
start with vendor='1';
具有讽刺意味的是,这一举动只是从第一行中删除了循环标志。任何帮助将不胜感激。
WHERE VENDOR <> CUSTOMER
呢? - Conrad Frix