有没有办法检测 SQL 服务器中的分层查询中的循环?
Is there a way to detect a cycle in Hierarchical Queries in SQL Server?
在Oracle中,我们可以使用函数CONNECT_BY_ISCYCLE
来检测层次查询中的循环。我尝试在 SQL 服务器中做同样的事情。有办法吗?
非常感谢!
连接记录 ID/根据记录的 ROW_NUMBERs 构建位图,并根据 list/bitmap
验证每个新记录
create table t (id int,pid int)
insert into t values (1,3),(2,1),(3,2)
列表
识别周期
with cte (id,pid,list,is_cycle)
as
(
select id,pid,',' + cast (id as varchar(max)) + ',',0
from t
where id = 1
union all
select t.id,t.pid,cte.list + cast (t.id as varchar(10)) + ',' ,case when cte.list like '%,' + cast (t.id as varchar(10)) + ',%' then 1 else 0 end
from cte join t on t.pid = cte.id
where cte.is_cycle = 0
)
select *
from cte
where is_cycle = 1
id pid list is_cycle
-- --- ---- --------
1 3 ,1,2,3,1, 1
循环遍历图
with cte (id,pid,list)
as
(
select id,pid,',' + cast (id as varchar(max)) + ','
from t
where id = 1
union all
select t.id,t.pid,cte.list + cast (t.id as varchar(10)) + ','
from cte join t on t.pid = cte.id
where cte.list not like '%,' + cast (t.id as varchar(10)) + ',%'
)
select *
from cte
id pid list
1 3 ,1,
2 1 ,1,2,
3 2 ,1,2,3,
位图
ID 应该是从 1 开始的数字序列。
如有必要,使用 ROW_NUMBER.
生成它
识别周期
with cte (id,pid,bitmap,is_cycle)
as
(
select id,pid,cast (power(2,id-1) as varbinary(max)) ,0
from t
where id = 1
union all
select t.id,t.pid,cast (cte.bitmap|power(2,t.id-1) as varbinary(max)),case when cte.bitmap & power(2,t.id-1) > 0 then 1 else 0 end
from cte join t on t.pid = cte.id
where cte.is_cycle = 0
)
select *
from cte
where is_cycle = 1
id pid bitmap is_cycle
1 3 0x00000007 1
循环遍历图
with cte (id,pid,bitmap)
as
(
select id,pid,cast (power(2,id-1) as varbinary(max))
from t
where id = 1
union all
select t.id,t.pid,cast (cte.bitmap|power(2,t.id-1) as varbinary(max))
from cte join t on t.pid = cte.id
where cte.bitmap & power(2,t.id-1) = 0
)
select *
from cte
id pid bitmap
1 3 0x00000001
2 1 0x00000003
3 2 0x00000007
如果您查看层次结构中的特定路径,您可以说它是一个 singled/doubled 链表(通常是单链表)。
一种确保没有闭环的简单方法是通过两条路径遍历链,每条路径都有自己的索引,一条前进一个位置,另一条前进两个。
如果没有闭环,其中一个索引将在某个点下降(例如,将到达没有任何父级的层次结构的根节点)。如果存在循环,您将到达一个点,其中两个索引指向链中的同一节点。
这是一个相当古老的方法,但效果很好。
在Oracle中,我们可以使用函数CONNECT_BY_ISCYCLE
来检测层次查询中的循环。我尝试在 SQL 服务器中做同样的事情。有办法吗?
非常感谢!
连接记录 ID/根据记录的 ROW_NUMBERs 构建位图,并根据 list/bitmap
验证每个新记录create table t (id int,pid int)
insert into t values (1,3),(2,1),(3,2)
列表
识别周期
with cte (id,pid,list,is_cycle)
as
(
select id,pid,',' + cast (id as varchar(max)) + ',',0
from t
where id = 1
union all
select t.id,t.pid,cte.list + cast (t.id as varchar(10)) + ',' ,case when cte.list like '%,' + cast (t.id as varchar(10)) + ',%' then 1 else 0 end
from cte join t on t.pid = cte.id
where cte.is_cycle = 0
)
select *
from cte
where is_cycle = 1
id pid list is_cycle
-- --- ---- --------
1 3 ,1,2,3,1, 1
循环遍历图
with cte (id,pid,list)
as
(
select id,pid,',' + cast (id as varchar(max)) + ','
from t
where id = 1
union all
select t.id,t.pid,cte.list + cast (t.id as varchar(10)) + ','
from cte join t on t.pid = cte.id
where cte.list not like '%,' + cast (t.id as varchar(10)) + ',%'
)
select *
from cte
id pid list
1 3 ,1,
2 1 ,1,2,
3 2 ,1,2,3,
位图
ID 应该是从 1 开始的数字序列。
如有必要,使用 ROW_NUMBER.
识别周期
with cte (id,pid,bitmap,is_cycle)
as
(
select id,pid,cast (power(2,id-1) as varbinary(max)) ,0
from t
where id = 1
union all
select t.id,t.pid,cast (cte.bitmap|power(2,t.id-1) as varbinary(max)),case when cte.bitmap & power(2,t.id-1) > 0 then 1 else 0 end
from cte join t on t.pid = cte.id
where cte.is_cycle = 0
)
select *
from cte
where is_cycle = 1
id pid bitmap is_cycle
1 3 0x00000007 1
循环遍历图
with cte (id,pid,bitmap)
as
(
select id,pid,cast (power(2,id-1) as varbinary(max))
from t
where id = 1
union all
select t.id,t.pid,cast (cte.bitmap|power(2,t.id-1) as varbinary(max))
from cte join t on t.pid = cte.id
where cte.bitmap & power(2,t.id-1) = 0
)
select *
from cte
id pid bitmap
1 3 0x00000001
2 1 0x00000003
3 2 0x00000007
如果您查看层次结构中的特定路径,您可以说它是一个 singled/doubled 链表(通常是单链表)。
一种确保没有闭环的简单方法是通过两条路径遍历链,每条路径都有自己的索引,一条前进一个位置,另一条前进两个。
如果没有闭环,其中一个索引将在某个点下降(例如,将到达没有任何父级的层次结构的根节点)。如果存在循环,您将到达一个点,其中两个索引指向链中的同一节点。
这是一个相当古老的方法,但效果很好。