Oracle CONNECT BY递归并返回匹配值

问题描述 投票:1回答:3

在以下示例中:

TABLE

ID  NAME    ATTR
-----------------
1   A1      ROOT
2   A2  
3   A3      VALX
4   A4  
5   A5  
6   A6  

关系

ID  CHILD_ID    PARENT_ID
-------------------------
1      6            4
2      5            4
3      4            3
4      3            1
5      2            1

SCHEMA

SCHEMA

我需要一个查询来获取PARENT的ATTR列的值不同于null的值。提高水平,直到获得第一个比赛为止。例如ID为6:

ID   NAME    NAME_PARENT     ATTR_PARENT
-----------------------------------------
6     A6          A3            VALX 

我尝试过:

select T.ID, T.NAME, T2.NAME PARENT_NAME, T2.ATTR ATTR_PARENT
from TABLE T
INNER JOIN RELATIONSHIP R
ON R.CHILD_ID = T.ID
INNER JOIN TABLE T2
ON T2.ID = R.PARENT_D
WHERE T2.ATTR IS NOT NULL
START WITH T.ID = 6
CONNECT BY T.ID = PRIOR R.PARENTID 
--and R.PARENTID != prior T.ID

对不起,我的英语不好

sql oracle recursive-query
3个回答
2
投票

可以使用标准的递归SQL CTE(公用表表达式)来代替使​​用[几乎过时的CONNECT BY子句。

例如:

with
n (id, name, name_parent, attr_parent, parent_id, lvl) as (
  select t.id, t.name, b.name, b.attr, r.parent_id, 1
  from t
  join r on t.id = r.child_id
  join t b on b.id = r.parent_id
  where t.id = 6 -- starting node
 union all 
  select n.id, n.name, b.name, b.attr, r.parent_id, lvl + 1
  from n
  join r on r.child_id = n.parent_id
  join t b on b.id = n.parent_id
  where n.attr_parent is null
)
select id, name, name_parent, attr_parent 
from n
where lvl = (select max(lvl) from n)

结果:

ID  NAME  NAME_PARENT  ATTR_PARENT
--  ----  -----------  -----------
6   A6    A3           VALX       

供参考,我使用的数据脚本是:

create table t (
  id number(6),
  name varchar2(10),
  attr varchar2(10)
);

insert into t (id, name, attr) values (1, 'A1', 'ROOT');
insert into t (id, name, attr) values (2, 'A2', null);
insert into t (id, name, attr) values (3, 'A3', 'VALX');
insert into t (id, name, attr) values (4, 'A4', null);
insert into t (id, name, attr) values (5, 'A5', null);
insert into t (id, name, attr) values (6, 'A6', null);

create table r (
  id number(6),
  child_id number(6),
  parent_id number(6)
);

insert into r (id, child_id, parent_id) values (1, 6, 4);
insert into r (id, child_id, parent_id) values (2, 5, 4);
insert into r (id, child_id, parent_id) values (3, 4, 3);
insert into r (id, child_id, parent_id) values (4, 3, 1);
insert into r (id, child_id, parent_id) values (5, 2, 1);

0
投票

您可以在嵌入式视图(或CTE)中进行分层查询,并联接到主表中以查找关联的名称/属性值,同时跟踪根ID /名称;同时过滤掉空值:

select root_id as id,
  root_name as name,
  min(name) keep (dense_rank first order by lvl) as name_parent,
  min(attr) keep (dense_rank first order by lvl) as attr_parent
from (
  select connect_by_root(r.child_id) as root_id,
    connect_by_root(r.child_id) as root_name,
    t.id,
    t.name,
    t.attr,
    level as lvl
  from relationship r
  join your_table t on t.id = r.child_id
  connect by prior parent_id = child_id
  start with child_id = 6
)
where attr is not null
group by root_id, root_name;

        ID       NAME NAME_PARENT  ATTR_PARENT 
---------- ---------- ------------ ------------
         6          6 A3           VALX        

min()变体:

min(t.name) keep (dense_rank first order by lvl)

保留与最低lvl有关的值(从层次结构级别开始),而不是按字母顺序排列的第一个值。

您可以使用子查询分解(也称为CTE)而不是嵌入式视图来执行相同的操作;这也允许您更改为使用recursive子查询分解,而不是旧的connect-by语法:

with rcte (root_id, root_name, child_id, parent_id, name, attr, lvl) as (
  select t.id, t.name, child_id, parent_id, t.name, t.attr, 1
  from relationship r
  join your_table t on t.id = r.child_id
  where child_id = 6
  union all
  select rcte.root_id, rcte.root_name, r.child_id, r.parent_id, t.name, t.attr, rcte.lvl + 1
  from rcte
  join relationship r on r.child_id = rcte.parent_id
  join your_table t on t.id = r.child_id
)
select root_id as id,
  root_name as name,
  min(name) keep (dense_rank first order by lvl) as name_parent,
  min(attr) keep (dense_rank first order by lvl) as attr_parent
from rcte
where attr is not null
group by root_id, root_name;


        ID NAME         NAME_PARENT  ATTR_PARENT 
---------- ------------ ------------ ------------
         6 A6           A3           VALX        

(...与@TheImpaler的答案基本相同,除了使用keep而不是子查询外,只是为了区别而已。)

db<>fiddle


0
投票

这里是如何通过connect by一次完成全部操作-使用可用于这种查询的各种功能(包括connect_by_isleaf标志和connect_by_root伪列):

select  connect_by_root(r.child_id) as id,
        connect_by_root(t.name)     as name,
        t.name                      as name_parent,
        t.attr                      as attribute_parent
from    r join t on r.child_id = t.id
where   connect_by_isleaf = 1
start   with r.child_id = 6
connect by prior r.parent_id = r.id and prior t.attr is null
;

        ID NAME       NAME_PAREN ATTRIBUTE_
---------- ---------- ---------- ----------
         6 A6         A3         VALX   

我使用了@TheImpaler的答案中发布的表和数据(感谢create tableinsert语句!]

正如我在他的回答下评论的那样:递归with子句在SQL标准中,因此它比connect by具有一些优势。但是,每当用connect by可以完成相同的工作时,至少也应该以这种方式进行测试。在许多情况下,由于甲骨文随着时间的推移进行了许多优化,因此connect by会更快。

[一些开发人员避免使用connect by的原因是,他们没有花时间学习各种功能(例如我在这里使用的功能)。我认为这不是很好的理由。

© www.soinside.com 2019 - 2024. All rights reserved.