问题描述
由于 ORACLE 功能“CONNECT BY”,我想在查询中从子 ID 中检索祖父级(顶级)和父级的信息(ID 和代码)。最好的方法是检索完整的历史数据(同一查询中的所有祖先和孩子)。
这里是数据:
ID | CODE | PARENT_ID
5953 | COMPANY |
230928 | D | 5953
7246 | C | 230928
243928 | C.5 | 7246
240961 | C.3 | 7246
7287 | C.4 | 7246
7286 | C.2 | 7246
7285 | C.1 | 7246
这里是我想要的结果:
CHILDREN_ID | CHILDREN_CODE | PARENT_ID | PARENT_CODE | GRANDPARENT_CODE
5953 | COMPANY | | |
230928 | D | 5953 | COMPANY |
7246 | C | 230928 | D | COMPANY
243928 | C.5 | 7246 | C | D
240961 | C.3 | 7246 | C | D
7287 | C.4 | 7246 | C | D
7286 | C.2 | 7246 | C | D
7285 | C.1 | 7246 | C | D
我创建了这个查询:
SELECT ID AS "CHILDREN_ID",CODE AS "CHILDREN_CODE",PARENT_ID,PARENT_CODE,CONNECT_BY_ROOT CODE "GRANT_PARENT_CODE"
FROM PERSONS
WHERE LEVEL > 1
CONNECT BY PRIOR ID = PARENT_ID
但我没有检索到祖父母的正确信息。
你能帮我解决这个问题吗?
解决方法
这可以相对简单地使用递归子查询分解子句来完成:
WITH rsqfc ( children_id,children_code,parent_id,parent_code,grandparent_id,grandparent_code ) AS (
SELECT id,code,NULL,NULL
FROM persons
WHERE parent_id IS NULL
UNION ALL
SELECT p.id,p.code,r.children_id,r.children_code,r.parent_id,r.parent_code
FROM rsqfc r
INNER JOIN persons p
ON ( r.children_id = p.parent_id )
)
SELECT *
FROM rsqfc
对于样本数据:
CREATE TABLE persons(id,parent_id) AS
SELECT 5953,'COMPANY',NULL FROM DUAL UNION ALL
SELECT 230928,'D',5953 FROM DUAL UNION ALL
SELECT 7246,'C',230928 FROM DUAL UNION ALL
SELECT 243928,'C.5',7246 FROM DUAL UNION ALL
SELECT 240961,'C.3',7246 FROM DUAL UNION ALL
SELECT 7287,'C.4',7246 FROM DUAL UNION ALL
SELECT 7286,'C.2',7246 FROM DUAL UNION ALL
SELECT 7285,'C.1',7246 FROM DUAL;
输出:
CHILDREN_ID | CHILDREN_CODE | PARENT_ID | PARENT_CODE | GRANDPARENT_ID | GRANDPARENT_CODE ----------: | :------------ | --------: | :---------- | -------------: | :--------------- 5953 | COMPANY | null | null | null | null 230928 | D | 5953 | COMPANY | null | null 7246 | C | 230928 | D | 5953 | COMPANY 243928 | C.5 | 7246 | C | 230928 | D 240961 | C.3 | 7246 | C | 230928 | D 7287 | C.4 | 7246 | C | 230928 | D 7286 | C.2 | 7246 | C | 230928 | D 7285 | C.1 | 7246 | C | 230928 | D
dbfiddle here
,我想我做到了!查询本身看起来很可怕,但我会解释:
select p.id children_id,p.code children_code,p.parent_id,prior p.code parent_code,case when level = 2 then null
when level = 3 then
connect_by_root code
else
substr(sys_connect_by_path(code,'\'),instr(sys_connect_by_path(code,'\',1,2)+1,(instr(sys_connect_by_path(code,3)) - (instr(sys_connect_by_path(code,2)+1))
end grandparent_code,case when level = 2 then null
when level = 3 then connect_by_root to_char(id)
else substr(sys_connect_by_path(id,instr(sys_connect_by_path(id,(instr(sys_connect_by_path(id,3)) - (instr(sys_connect_by_path(id,2)+1))
end grandparent_id
from persons p
connect by prior p.id = p.parent_id
start with p.parent_id is null;
我确实使用了 connect_by_path 来显示从根到子条目的路径。有关详细信息,请参阅 doc。
它将提供类似 '..\grand-grand-parent\grand-parent\parent\child' 这样的路径,所以只剩下祖父母。例外情况是 level = 2(无祖父级)和 level3 足以仅显示根。
希望对你有帮助。无论如何,我为一个有趣的问题 +1
,计算层次结构中某些中间节点的属性的最简单方法是使用递归 with
,您可以控制每个步骤的计算。但是对于这种特殊情况,您只需要父级的属性,因此您可以对连接的第一个结果进行分层查询:
with persons(id,parent_id) as (
select 5953,null from dual union all
select 230928,5953 from dual union all
select 7246,230928 from dual union all
select 243928,7246 from dual union all
select 240961,7246 from dual union all
select 7287,7246 from dual union all
select 7286,7246 from dual union all
select 7285,7246 from dual
)
select
p.id children_id,pp.code parent_code,prior pp.code as grandparent_code
from persons p
left join persons pp
on p.parent_id = pp.id
start with p.parent_id is null
connect by prior p.id = p.parent_id
CHILDREN_ID | CHILDREN_CODE | PARENT_ID | PARENT_CODE | GRANDPARENT_CODE ----------: | :------------ | --------: | :---------- | :--------------- 5953 | COMPANY | null | null | null 230928 | D | 5953 | COMPANY | null 7246 | C | 230928 | D | COMPANY 7285 | C.1 | 7246 | C | D 7286 | C.2 | 7246 | C | D 7287 | C.4 | 7246 | C | D 240961 | C.3 | 7246 | C | D 243928 | C.5 | 7246 | C | D
dbfiddle here