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 table
和 insert
声明!)
正如我在他的回答下评论的那样:递归 with
子句在 SQL 标准中,因此它比 connect by
有一些优势。但是,只要可以使用 connect by
完成相同的工作,至少也值得以这种方式进行测试。在许多情况下,由于 Oracle 随着时间的推移进行了大量优化,connect by
会快得多。
一些开发人员避免 connect by
的一个原因是他们没有花时间学习各种功能(比如我在这里使用的功能)。在我看来,这不是一个很好的理由。
在下面的例子中:
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 table
和 insert
声明!)
正如我在他的回答下评论的那样:递归 with
子句在 SQL 标准中,因此它比 connect by
有一些优势。但是,只要可以使用 connect by
完成相同的工作,至少也值得以这种方式进行测试。在许多情况下,由于 Oracle 随着时间的推移进行了大量优化,connect by
会快得多。
一些开发人员避免 connect by
的一个原因是他们没有花时间学习各种功能(比如我在这里使用的功能)。在我看来,这不是一个很好的理由。