Oracle层次结构获取每个id的所有children和所有parents
Oracle Hierarchy to get all children and all parents of each id
我有一个 table 和 parent/child 个 ID,我正在尝试获取 parents 和 children 所有级别的完整列表给定 id.
基本上,对于给定的 id,一直向下并一直向上。
我试过连接方式,但也许递归 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 是 child 和 parent 路径的串联 + 子字符串,这似乎有点老套。另外,我得到了我不确定如何过滤掉的额外结果(例如: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(作为输入 - 下面我在查询中使用绑定变量),您需要找到它的所有叶子后代,然后对于每个这样的叶子,显示来自的完整路径叶子到层次结构的根。
一种方法是遍历层次结构两次:首先从给定的 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
;
这里有一种方法,它首先沿着树下降,从所选树中获取根 parent(s)。
然后利用这些树根重新爬上去。
然后根据所选 child.
过滤生成的路径
剩下的是那些最后 child 不是 parent 的那些。
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
演示 db<>fiddle here
另一种方法。
这次通过递归 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
演示 db<>fiddle here
我有一个 table 和 parent/child 个 ID,我正在尝试获取 parents 和 children 所有级别的完整列表给定 id.
基本上,对于给定的 id,一直向下并一直向上。
我试过连接方式,但也许递归 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 是 child 和 parent 路径的串联 + 子字符串,这似乎有点老套。另外,我得到了我不确定如何过滤掉的额外结果(例如: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(作为输入 - 下面我在查询中使用绑定变量),您需要找到它的所有叶子后代,然后对于每个这样的叶子,显示来自的完整路径叶子到层次结构的根。
一种方法是遍历层次结构两次:首先从给定的 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
;
这里有一种方法,它首先沿着树下降,从所选树中获取根 parent(s)。
然后利用这些树根重新爬上去。
然后根据所选 child.
过滤生成的路径剩下的是那些最后 child 不是 parent 的那些。
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 |
演示 db<>fiddle here
另一种方法。
这次通过递归 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 |
演示 db<>fiddle here