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 等等)
我有一个 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 等等)