从 Oracle 中具有级别的子 ID 中获取 GrandParent 和 Parent 信息

问题描述

由于 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

dbfiddle

,

计算层次结构中某些中间节点的属性的最简单方法是使用递归 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