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

时间:2017-09-06 18:14:34

标签: sql oracle11g recursive-query

我有一个表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,更长。

如何编写一个返回给定长度的所有链的查询?

我开始喜欢

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))

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

3 个答案:

答案 0 :(得分:1)

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)
  • 6/7是lvl 2(有些人可能会说这是1,依此类推)

答案 1 :(得分:0)

您可以使用递归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

答案 2 :(得分:0)

如果链接数量有限,可能只是加入一个超出您认为需要的数量,而不是递归地写它:

 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