在以下示例中:
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(公用表表达式)来代替使用[几乎过时的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);
您可以在嵌入式视图(或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
而不是子查询外,只是为了区别而已。)
这里是如何通过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 table
和insert
语句!]
正如我在他的回答下评论的那样:递归with
子句在SQL标准中,因此它比connect by
具有一些优势。但是,每当用connect by
可以完成相同的工作时,至少也应该以这种方式进行测试。在许多情况下,由于甲骨文随着时间的推移进行了许多优化,因此connect by
会更快。
[一些开发人员避免使用connect by
的原因是,他们没有花时间学习各种功能(例如我在这里使用的功能)。我认为这不是很好的理由。