【问题标题】:Get GrandParent and Parent info from the children id with level in Oracle从 Oracle 中具有级别的子 ID 获取 GrandParent 和 Parent 信息
【发布时间】:2021-04-16 06:50:30
【问题描述】:

借助 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

但我没有检索到祖父母的正确信息。

你能帮我解决这个问题吗?

【问题讨论】:

  • 您好,请向我们提供测试数据和所需的输出
  • 你需要start with parent_id is null从根建立一个层次结构并使用joinpersons表来获取父级的父级,因为connect_by_root从一个根目录中检索信息等级制度

标签: sql oracle hierarchical-data levels


【解决方案1】:

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

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 |父 ID |父代码 |祖父 ID | GRANDPARENT_CODE ----------: | :------------ | --------: | :------------ | -------------: | :--------------- 5953 |公司 | | | | 230928 | D | 5953 |公司 | | 7246 | C | 230928 | D | 5953 |公司 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小提琴here

【讨论】:

    【解决方案2】:

    我想我成功了!查询本身看起来很吓人,但我会解释一下:

    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 来显示从根到子条目的路径。详情请见doc

    它将提供类似 '..\grand-grand-parent\grand-parent\parent\child' 这样的路径,所以只剩下祖父母了。例外情况是级别 = 2(无祖父)和级别 3 时仅显示根即可。

    我希望这会对你有所帮助。无论如何,我为一个有趣的问题 +1

    dbfiddle

    【讨论】:

    • 谢谢,我试过这个查询,但不幸的是我没有得到结果
    • @coeurdange57 嗯。它确实适用于您提供的测试数据。我添加了指向 dbfiddle 的链接,所以请检查一下,也许您需要修复测试数据中的某些内容,或者您​​需要根据您的真实数据调整我的答案
    • 对不起,我错了,我已经测试过了,它正在工作。我只是添加 START WITH 5953 (实际上顶层的代码是 null 而不是 "Company" )。但是您能告诉我如何在查询中添加 grandparent_id 吗?
    • @coeurdange57 您只需在 grandparent_code 列计算中将“code”替换为“id”即可。我已经更新了我的答案 grandparent_id 列
    【解决方案3】:

    计算层次结构中某些中间节点的属性的最简单方法是使用递归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 |父 ID |父代码 | GRANDPARENT_CODE ----------: | :------------ | --------: | :------------ | :--------------- 5953 |公司 | | | 230928 | D | 5953 |公司 | 7246 | C | 230928 | D |公司 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小提琴here

    【讨论】:

      猜你喜欢
      • 2021-07-10
      • 1970-01-01
      • 2013-12-15
      • 1970-01-01
      • 2011-03-12
      • 1970-01-01
      • 2020-03-06
      • 2015-07-29
      • 2020-12-08
      相关资源
      最近更新 更多