【问题标题】:Oracle Hierarchy to get all children and all parents of each idOracle Hierarchy 获取每个 id 的所有孩子和所有父母
【发布时间】:2022-01-15 21:55:09
【问题描述】:

我有一张带有父/子 ID 的表格,我正在尝试获取给定 ID 的所有级别的父子和子节点的完整列表。
基本上,对于给定的 id,从层次结构一直向下一直向上。

我试过 connect by,但也许递归 CTE 会更好?

select 'abc' as child, null as parent from dual union all
select 'mno' as child, 'abc' as parent from dual union all
select 'def' as child, 'abc' as parent from dual union all
select '123' as child, 'abc' as parent from dual union all
select 'qrs' as child, '123' as parent from dual union all
select '789' as child, 'def' as parent from dual union all
select 'xyz' as child, '123' as parent from dual 

例如:

Child Parent
abc null
mno abc
def abc
123 abc
qrs 123
789 def
xyz 123

对于 123,所需的输出:

  • xyz > 123 > abc
  • qrs > 123 > abc

对于 abc,所需的输出:

  • xyz > 123 > abc
  • 789 > def > abc
  • qrs > 123 > abc
  • mno > abc

这是我的尝试。 full_hier 是子路径和父路径的串联 + 子字符串,这似乎有点 hacky。另外,我得到了一些我不确定如何过滤掉的额外结果(例如:def > abc 被返回,但我不想要它,因为它是在 789 > def > abc 中捕获的)。

select 
    connect_by_root child,
    substr(sys_connect_by_path(child, '>' ),2) as child_hier
    , substr(sys_connect_by_path(parent, '>' ),2) as parent_hier
    , case 
        when parent is null then substr(sys_connect_by_path(child, '>' ),2)
        else substr(sys_connect_by_path(child, '>' ),2) ||  substr(substr(sys_connect_by_path(parent, '>' ),2), instr(substr(sys_connect_by_path(parent, '>' ),2),'>',1,1)) 
    end as full_hier
    , level
from   
    (
        select 'abc' as child, null as parent from dual union all
        select 'mno' as child, 'abc' as parent from dual union all
        select 'def' as child, 'abc' as parent from dual union all
        select '123' as child, 'abc' as parent from dual union all
        select 'qrs' as child, '123' as parent from dual union all
        select '789' as child, 'def' as parent from dual union all
        select 'xyz' as child, '123' as parent from dual 
    ) table_name
where 1=1
--and connect_by_isleaf = 1
--and connect_by_root child in ('123')
and child = 'abc'
connect by child = prior parent 
--connect_by prior parent = child

感谢观看,不胜感激!

【问题讨论】:

  • “孩子”是指“严格”的孩子(不包括给定的 id 本身作为它自己的孩子)?那么它不是真正的“在所有级别”,而是“在级别 > 1” - 给定的 id 是它自己的子级 = 0。然后,如果给定的 id 是叶子(没有严格的子级),则查询将不返回任何内容(没有行)?
  • 哦 - 实际上您也不想为不是叶子的孩子显示一行。似乎您只想显示给定 id 的叶子后代的路径,它们不等于给定的 id。 (换句话说,如果给定的 id 已经是叶子,则不返回任何行)。是这样吗?

标签: sql oracle oracle11g hierarchical-data


【解决方案1】:

另一种方法。
这次通过递归 CTE。

with cte_init (base) as (
    select '123' as base 
    from dual
), 
rcte_hierarchy_down (base, lvl, child, parent) as (
   select 
     child as base
   , 0 as lvl
   , child
   , parent
   from test_hierarchy
   where child in (select base from cte_init) 
   
   union all
   
   select 
     cte.base
   , cte.lvl-1
   , t.child
   , t.parent
   from rcte_hierarchy_down cte
   join test_hierarchy t 
     on t.child = cte.parent
), 
rcte_hierarchy_up (lvl, child, parent, path) as (
   select 
   1 as lvl
   , child
   , parent
   , child||'>'||parent as path
   from test_hierarchy h
   where parent in (select child 
                    from rcte_hierarchy_down 
                    where parent is null) 
   
   union all
   
   select 
     cte.lvl+1
   , t.child
   , t.parent
   , t.child||'>'||cte.path
   from rcte_hierarchy_up cte
   join test_hierarchy t 
     on t.parent = cte.child
)
select distinct h.path
from rcte_hierarchy_up h
join cte_init i on h.path like '%'||i.base||'%'
and not exists (
    select 1 
    from test_hierarchy t
    where t.parent = h.child
  )
PATH
qrs>123>abc
xyz>123>abc

dbfiddle here

上的演示

【讨论】:

  • 这很好用,谢谢!它在约 500k 的记录集上运行得非常快。
【解决方案2】:

如果我理解正确,给定任何 id(作为输入 - 下面我在查询中使用绑定变量),您需要找到它的所有叶子后代,然后对于每个这样的叶子,显示从叶子到的完整路径层次结构的根。

一种方法是遍历层次结构两次:首先从给定的 id 开始并查找其所有叶子后代,然后沿相反方向遍历以查找所有“完整路径”。

虽然这可能看起来(略微)更优雅,但效率会大大降低。更好的方法是按照您已经尝试过的方法。

下面我使用with 子句(并在子查询声明中给出列名 - 仅从 Oracle 11.2 开始支持,如果您的版本是 11.1,则需要将别名移动到 select 子句中在你的尝试中做)。

with
  table_name (child, parent) as (
    select 'abc', null  from dual union all
    select 'mno', 'abc' from dual union all
    select 'def', 'abc' from dual union all
    select '123', 'abc' from dual union all
    select 'qrs', '123' from dual union all
    select '789', 'def' from dual union all
    select 'xyz', '123' from dual   
  )
, a (ancestor_path) as (
    select  sys_connect_by_path(child, '>')
    from    table_name
    where   connect_by_isleaf = 1
    start   with child = :i_child
    connect by child = prior parent
  )
, d (descendant_path) as (
    select  substr(sys_connect_by_path(child, '>'), 2)
    from    table_name
    where   connect_by_isleaf = 1
    start   with parent = :i_child
    connect by parent = prior child
  )
select d.descendant_path || a.ancestor_path as full_path
from   d cross join a
;

【讨论】:

  • 我对 Oracle 很陌生,我对 presto 更熟悉 - 我会检查 :i_child 声明。这是否适用于查看 ID 列表,还是我需要运行单个 ID 的循环?
【解决方案3】:

这是一种方法,它首先从树中下降以从选定的父节点中获取根父节点。

然后用那些根重新爬上去。

然后根据所选子级过滤生成的路径。

剩下的是那些最后一个孩子不是父母的人。

create table test_hierarchy (
 child varchar(3),
 parent varchar(3),
 primary key (child)
);

insert into test_hierarchy (child, parent)
select 'abc' as child, null as parent from dual union all
select 'mno' as child, 'abc' as parent from dual union all
select 'def' as child, 'abc' as parent from dual union all
select '123' as child, 'abc' as parent from dual union all
select 'qrs' as child, '123' as parent from dual union all
select '789' as child, 'def' as parent from dual union all
select 'xyz' as child, '123' as parent from dual;
with cte (base) as (
 select '123' from dual
)
select *
from
(
  select 
    sys_connect_by_path(child,'>') as path
  , cte.base
  , level
  , connect_by_root child as child
  from test_hierarchy
  cross join cte
  where child in (
    select connect_by_root child 
    from test_hierarchy
    where child in (select base from cte)
    and connect_by_root parent is null
    connect by prior child = parent 
  ) 
  connect by prior parent = child
) q
where path like '%'||base||'%'
  and not exists (
    select 1 
    from test_hierarchy t
    where t.parent = q.child
  ) 
PATH BASE LEVEL CHILD
>qrs>123>abc 123 3 qrs
>xyz>123>abc 123 3 xyz

dbfiddle here

上的演示

【讨论】:

  • 我尝试过类似的方法,但它运行了 5 分钟以上,所以我放弃了它。不过,它与样本数据配合得很好。感谢您的选择!
猜你喜欢
  • 2021-07-04
  • 2018-01-29
  • 1970-01-01
  • 2018-04-19
  • 1970-01-01
  • 2011-12-19
  • 1970-01-01
  • 1970-01-01
  • 2021-12-02
相关资源
最近更新 更多