Oracle CONNECT BY递归和return值匹配

Oracle CONNECT BY recursive and return value a match

在下面的例子中:

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

架构

我需要一个查询来获取 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 CTE(通用 Table 表达式)而不是使用 [大部分已过时] 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 = r.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);

以下是如何在 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.child_id and prior t.attr is null
;

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

请注意,如果遍历整个树而没有找到具有非空属性的祖先,这仍然会 return null ATTRIBUTE_PARENT。事实上,如果您只想在祖先具有非空 ATTRIBUTE 时在输出中显示某些内容(如果没有这样的祖先,则允许输出没有行),您可以将 where 子句更改为 where t.attr is not null。不过,在大多数情况下,您可能想要我编写代​​码时的行为。

我使用了@TheImpaler 的回答中发布的表格和数据(感谢您的 create tableinsert 声明!)

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

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