查找所有直接和间接结束child

Find all direct and indirect end child

我有 table 这样的 :

ID Child ID Flag
A01 C01 Y
A01 C02 Y
A01 AC01 N
AC01 C03 Y
AC01 AC02 N
AC02 C04 Y

所以这里的A01是直接parent的C01,C02,AC01。 AC01是C03和AC02的直接parent,AC02是C04.C01、C02、C03的直接parent和C04 没有任何 child。所以他们就像end children。这就是为什么他们的 标志是 'Y'。但是AC01和AC02不像纯端child。而且C03和C04是A01的间接child任 .

I want to list down all direct and indirect end children(Flag ='Y') of a particular ID.Something like below:

ID Child ID
A01 C01
A01 C02
A01 C03
A01 C04

我使用的数据库Oracle 11G

我试过 递归 WITH 子句。

WITH  child (id,child_id) AS (
    SELECT  id,
            child_id,
            0 AS level
    FROM my_table
 
    UNION ALL
 
    SELECT  e.id, 
            e.child_id,
            level + 1
    FROM my_table e 
JOIN child s 
ON e.id = s.child_id
)
 
SELECT 
    m.id,
    s.child_id
FROM child s 
JOIN my_table m 
ON s.id = m.child_id;

但是我没有得到想要的输出。

因此,首先概括一下您的数据模型,您通常希望有一个 table 和一个 ID 作为 主键 ,一个额外的列指向到 parent_id.

够了,IDleaf(你所谓的 end child)没有必要存储为可以查询。

根据你的数据,你会得到这样的结果

create table tab as
select  'A01' id, null parent_id from dual union all
select  'C01' id, 'A01' parent_id from dual union all
select  'C02' id, 'A01' parent_id from dual union all
select  'AC01' id, 'A01' parent_id from dual union all
select  'C03' id, 'AC01' parent_id from dual union all
select  'AC02' id, 'AC01' parent_id from dual union all
select  'C04' id, 'AC02' parent_id from dual ;

ID   PARENT_ID
---- ---------
A01      
C01  A01 
C02  A01 
AC01 A01 
C03  AC01
AC02 AC01
C04  AC02

现在 递归查询 获取所有 ID 直接或间接从节点说 A01

WITH  rec (root_id, id, lvl) AS (
    SELECT  id as root_id,
            id,
            0 AS lvl
    FROM tab where id = 'A01'
    UNION ALL 
    SELECT  rec.root_id root_id, 
            tab.id,
            lvl + 1
    FROM tab 
    JOIN rec
    ON tab.parent_id  = rec.id
)
 
SELECT 
    rec.root_id,
    rec.id,
    rec.lvl
FROM rec 

ROOT ID          LVL
---- ---- ----------
A01  A01           0
A01  C01           1
A01  C02           1
A01  AC01          1
A01  C03           2
A01  AC02          2
A01  C04           3

请注意,在递归查询的锚点部分,您必须限制要开始的节点(您没有这样做)。

这个问题不需要递归 with 子句。这是可以做到的,但识别“终结”儿童并非易事。

相反,老式的 connect by 查询(分层查询)可以快速完成这项工作。请注意,在我的查询(和输出)中,我没有包含 ID 列 - 如果您愿意,可以包含它,但它绝对不会给您任何信息;它只是将输入 ('A01') 复制到列中。这是您在 运行 查询之前就会知道的事情 - 实际上,甚至在编写查询之前!

关键是where子句;在层次结构中,“结束”子级被称为 leavesconnect_by_isleaf returns 1 代表叶子,0 否则。

select  child_id
from    my_table
where   connect_by_isleaf = 1
start   with id = 'A01'
connect by id = prior child_id
;

如果您坚持使用递归 with 子句的解决方案,这里有一种方法。请注意末尾的 search 子句 - 它是在下一个子查询中正确定义 is_leaf 列的关键。这种方法的一个优点是它使用 SQL 标准功能(递归 with 子句)模仿 Oracle 专有的分层查询 (connect by)。

with
  r (child_id, lvl) as (
    select  child_id, 1
      from  my_table
      where id = 'A01'
    union all
    select  t.child_id, r.lvl + 1
      from  my_table t join r on t.id = r.child_id
  )
  search depth first by child_id set ord
, prep (child_id, is_leaf) as (
    select child_id,
           case when lead(lvl) over (order by ord) > lvl then 0 else 1 end
    from   r
  )
select child_id
from   prep
where  is_leaf = 1
;