从具有 Oracle 级别的 children id 获取 GrandParent 和 Parent 信息

Get GrandParent and Parent info from the children id with level in Oracle

我想从查询中的 Children ID 中检索 Grand Parent(TOP Level)和 Parent 的信息(ID 和 CODE),感谢ORACLE 功能“CONNECT BY”。最好的办法是检索完整的历史数据(所有祖先和 children 在同一个查询中)。

这里是数据:

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

但我没有检索到 Grand parent 的正确信息。

你能帮我吗?

我想我做到了!查询本身看起来很可怕,但我会解释:

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, '\'), '\', 1, 3)) - (instr(sys_connect_by_path(code, '\'), '\', 1, 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, '\'), '\', 1, 2)+1, 
                 (instr(sys_connect_by_path(id, '\'), '\', 1, 3)) - (instr(sys_connect_by_path(id, '\'), '\', 1, 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 来显示从根目录到 child 条目的路径。详情见doc

它将提供类似“..\grand-grand-parent\grand-parent\parent\child”的路径,因此只需替换掉 grand parent 即可。例外情况是 level = 2(无 grand-parent)和 level3 足以显示根。

希望对您有所帮助。不管怎样,我对一个有趣的问题 +1

dbfiddle

这可以使用递归子查询分解子句相对简单地完成:

WITH rsqfc ( children_id, children_code, parent_id, parent_code, grandparent_id, grandparent_code ) AS (
  SELECT id,
         code,
         NULL,
         NULL,
         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, code, 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               

db<>fiddle here

计算层次结构中某些中间节点的属性的最简单方法是使用递归 with,您可以控制每一步的计算。但是对于这种特殊情况,您只需要父级的属性,因此您可以对连接的第一个结果进行分层查询:

with persons(id, code, 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    
)

select
  p.id children_id, 
  p.code children_code, 
  p.parent_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               

db<>fiddle here