Oracle递归查询查找给定长度的链

Oracle recursive query to find chain of given length

我有一个 table TABLE,它有两个字段 OLD_ID,NEW_ID 表示合并数据的 ID。

我们可以

(OLD_ID='1',NEW_ID='20')

(OLD_ID='4',NEW_ID='50')

(OLD_ID='50',NEW_ID='70')

等没有无限循环,但可能有许多原始 ID 的合并,因此链可以是 2、3、4,甚至更长。

如何编写 returns 给定长度的所有链的查询?

我开始像

select * from TABLE t1 as m1 where NEW_ID in
(select OLD_ID from t1 as m2 where OLD_ID in
(select NEW_ID from t1 as m3 where m1.NEW_ID <> NEW_ID))

但这不起作用并且不可扩展。

您可以为此使用递归 CTE:

With Cte As 
(
    Select  Old_Id, New_Id, 1 As Level
    From    YourTable
    Where   Old_Id = 4
    Union All
    Select  T2.Old_Id, T2.New_Id, T1.Level + 1
    From    Cte         T1
    Join    YourTable   T2  On  T1.New_Id = T2.Old_Id
)
Select   *
From     Cte
Order By Level

与其递归地编写,如果链接数量有限,也许只需加入一个比您认为需要的多的链接:

 select T1.OLD_ID as ID1
      , T1.NEW_ID as ID2
      , T2.NEW_ID as ID3
      , T3.NEW_ID as ID4
      , T4.NEW_ID as ID5
      , T5.NEW_ID as ID6
      , T6.NEW_ID as ID7
 from table T1
 left join table T2
    on T1.NEW_ID = T2.OLD_ID
 left join table T3
    on T2.NEW_ID = T3.OLD_ID
 left join table T4
    on T3.NEW_ID = T4.OLD_ID
 left join table T5
    on T4.NEW_ID = T5.OLD_ID
 left join table T6
    on T5.NEW_ID = T6.OLD_ID    

Oracle 可以使用 connect by 来执行此操作:Connect by 提供了几个可能也有帮助的伪列。

with CTE (Old_ID, New_ID) as (
Select 0,1 from dual union all
Select 1,2 from dual union all
Select 2,3 from dual union all
Select 3,4 from dual union all
Select 4,5 from dual union all
Select 5,6 from dual union all 
Select 7,8 from dual union all
Select 8,9 from dual union all
Select 9,10 from dual union all
Select 10,11 from dual union all 
Select 12,13 from dual)

SELECT Old_ID OID
     , New_ID NID
     , level lvl
     , SYS_CONNECT_BY_PATH(OLD_ID, '/') "Path"  --not needed but nice pseudo col 
     , CONNECT_BY_ISLEAF "IsLeaf" --needed to exclude subchains.
FROM cte A  --Update this line w/ your table name
WHERE   --LEVEL = 1 and 
   CONNECT_BY_ISLEAF =1  --this is how we only look at chains that have no other siblings
Start with OLD_ID not in (Select new_ID from CTE)  --This ensures we look only at values that are themselves not new_IDs In other words, the oldest ancestor). -- and update the cte table name in the subquery here to your table name
connect by NOCYCLE prior new_ID=OLD_ID   --added nocycle.

因为这允许您只遍历整个结构一次。查看整个链条的长度,而不是将其细分。 isleaf 确保我们在确定链长度时只查看具有完整链的记录。

这给了我们:

+----+----+---+--------------+----+
|OID |NID |LVL| Path         |leaf|
+----+----+---+--------------+----+
|  5 |  6 | 6 | /0/1/2/3/4/5 | 1  |
| 10 | 11 | 4 | /7/8/9/10    | 1  |
| 12 | 13 | 1 | /12          | 1  |
+----+----+---+--------------+----+

您可以取消注释 level = 1 并替换为所需级别。

您可能需要减去 1,具体取决于您计算级别的方式。有些人喜欢从0开始;这从 1 开始。

  • a 5/6 是 lvl 1(有些人可能会说这是 0)
  • a 6/7 是 lvl 2(有些人可能会说这是 1 等等)