Oracle 对树形数据的支持及用法示例

树形数据示例

日常工作中,经常遇到树形结构的数据,即每条记录(或节点)都存在一个 Parent_ID字段,用于标识其父节点的ID。例如:

with node_list as (
    select 1 id, null parent_id, 'node1' name from dual union all
    select 2 id, 1 parent_id, 'node1-2' name from dual union all
    select 3 id, 1 parent_id, 'node1-3' name from dual union all
    select 4 id, 2 parent_id, 'node1-2-4' name from dual union all
    select 5 id, 2 parent_id, 'node1-2-5' name from dual union all
    select 6 id, 5 parent_id, 'node1-2-5-6' name from dual union all
    select 7 id, 0 parent_id, 'node0-7' name from dual union all
    select 8 id, 9 parent_id, 'node9-8' name from dual union all
    select 9 id, 8 parent_id, 'node8-9' name from dual
)
select * from node_list t;

注意: 以下SQL直接采用 临时表 node_list,省略 with node_list as… 子句。
查询结果:
在这里插入图片描述
对于此类数据,我们经常需要明确当前节点所在树的位置、树的根在哪里、当前节点是叶子还是分叉?

Connect By

如果希望还原树形结构的原貌,可以通过Connect By 建立前后记录的父子关系:

select * from node_list t
connect by prior t.id = t.parent_id;

但是,在本文例子中,存在两个特殊节点:node9-8 和 node8-9,这两个节点互相成为对方的Parent,这种情况属于循环依赖。如果直接查询,将会报错:
存在循环依赖时的报错
此时,应通过 NOCYCLE 关键字,避免循环依赖报错。

select * from node_list t
connect by NOCYCLE prior t.id = t.parent_id;

查询结果:
在这里插入图片描述

Start With

通过Start With,我们可以限定查询树的起始节点。比如,可以从 node1开始查询:

select * from node_list t
start with t.parent_id is null
connect by NOCYCLE prior t.id = t.parent_id;

查询结果:
在这里插入图片描述
这样就能看到当前节点及其向下的所有子节点。

Level & Path

既然是树状结构,那么每个节点都有对应的级别(Level 直译),如当前为1,则其子、孙节点的级别分别为2、3,此处Level 或者翻译成辈份、代级更合适。例如:

select t.*, level from node_list t
start with t.id = 2
connect by NOCYCLE prior t.id = t.parent_id;

查询结果:
在这里插入图片描述
为了避免混淆,我们在给节点name命名时,刻意体现了其父子关系。但是树状结构数据本身可以通过sys_connect_by_path 函数来体现这种关系:

select t.*, level, sys_connect_by_path(t.id, '/') curr_path from node_list t
start with t.id = 1
connect by NOCYCLE prior t.id = t.parent_id;

查询结果:
在这里插入图片描述
由上可见,每个节点所在树的完整路径。
其他关键字:

  • CONNECT_BY_ISLEAF:判断当前节点是否为叶子节点(即无子嗣的、无后续节点的);
  • CONNECT_BY_ISCYCLE:检查当前节点是否存在上下层互为父子节点,即循环依赖的情况,详见循环依赖章节
  • CONNECT_BY_ROOT:与某个字段搭配,以获取根节点的对应字段信息。

举例:

select t.*, connect_by_isleaf, connect_by_root(name)
  from node_list t
start with t.id = 1
connect by NOCYCLE prior t.id = t.parent_id;

查询结果:
在这里插入图片描述
综合运用这些关键字,可以帮助我们处理树状结构数据,实现相应的业务功能。

检测异常节点

循环依赖

本文例子中的数据 node9-8和node8-9,是一个循环依赖的例子:

select t.*, connect_by_iscycle
  from node_list t
start with t.id = 8
connect by NOCYCLE prior t.id = t.parent_id;

查询结果:
在这里插入图片描述
通过这种方式,可以检查和过滤存在循环依赖的节点,避免程序加载数据时出错。

节点丢失

在根节点上,我们默认其parent_id 为空,但是在误删数据等情况,可能导致根节点丢失。比如本文例子中的 node0-7,其parent_id为0,但id为0的记录是不存在的。这种情况,将可能导致该节点被丢掉。
例如:

select t.* from node_list t
start with t.parent_id is null
connect by NOCYCLE prior t.id = t.parent_id;

查询结果:
在这里插入图片描述
可以看到,在结果中,并不存在 node0-7节点。

Where条件过滤

结合 Where条件,我们可以从树状结构的表中提取出我们想要的树。

select t.*
  from node_list t 
  where t.name like 'node%'
start with t.parent_id is null
connect by NOCYCLE prior t.id = t.parent_id;

对于保存了大量事实数据、业务单据号的表来说,Where条件是不可缺少的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章