我有一个带有属性的对象表。一些对象共享相同的属性,在这种情况下,我认为它们是通过它们的公共属性连接的:
object attribute
1 a
1 b
2 a
2 c
3 b
3 d
4 c
5 d
6 e
7 e
将其视为网络:
5 - d - 3 - b - 1 - a - 2 - c - 4 7 - e - 6
在此示例中,网络只是一条线。它可以是一棵树,有圆圈等。我需要找到通过通用属性和其他对象与对象1
连接的所有对象的集合。在这种情况下,它就是集合(1, 2, 3, 4, 5)
我尝试使用递归:
with graph as (select object, attribute
from my_table where object = 1
union all
select my_table.object, my_table.attribute
from graph join my_table on my_table.attribute = graph.attribute
and my_table.object <> graph.object
)
select * from graph
它适用于非圆形图形,但是当我有圆形时,我会继续在graph
表中添加相同的元素。如何跟踪已添加的元素?
编辑:它实际上不适用于任何图形,因为到达元素3
后,它返回到元素1
并在它们之间反弹。
更新:答案对我没有用,因为它一次又一次地通过相同的节点(对象之间存在循环关系),但这使我走上了正轨!在与SQL长期斗争之后,不允许在CTE中做任何事情,我发现这对于我的图形来说足够快地执行:
drop table if exists #my_table
create table #my_table
(obj int
,attribute varchar(10)
)
insert into #my_table values
(1, 'a'),
(1, 'b'),
(2, 'a'),
(2, 'c'),
(3, 'b'),
(3, 'd'),
(4, 'c'),
(5, 'd'),
(6, 'e'),
(7, 'e')
;with grouped as (select 1 step
-- this will be the full history of all objects and attributes that are connected to the initial object
,convert(varchar(max), stuff((select ', ' + convert(varchar(10), dc.obj) + ' - ' + dc.attribute
FROM #my_table dc
where obj = 1
for xml path ('')), 1, 2, '')
) history
union ALL
SELECT step + 1 as step
,convert(varchar(max), grouped.history + (SELECT ', ' + convert(varchar(10), dc2.obj) + ' - ' + dc2.attribute
from #my_table dc
JOIN #my_table dc2 on dc2.obj = dc.obj
-- the new object should be connected to an attribute in our history
WHERE grouped.history like '%' + convert(varchar(10), dc.attribute) + '%'
-- but this should be a new object (not in the history yet)
and grouped.history not like '%' + convert(varchar(10), dc2.obj) + ' - ' + dc.attribute + '%'
for xml path (''))
) history
FROM grouped
where grouped.history is not null
)
select * from grouped
答案 0 :(得分:1)
有必要存储有关经过的节点的信息。
declare @my table(obj int, attr char(1));
insert into @my(obj,attr) values
(1,'a'),
(1,'b'),
(2,'a'),
(2,'c'),
(3,'b'),
(3,'d'),
(4,'c'),
(5,'d'),
(6,'e'),
(7,'e');
with
t as(
select 1 lv,obj,attr,row_number() over(order by(select 0)) gr,
cast('/'+str(obj,5)+right(space(4)+attr,5)+'/' as varchar(max)) hy
from @my
where obj=5
union all
select t.lv+1,my.obj,my.attr,t.gr,
t.hy+str(my.obj,5)+right(space(4)+my.attr,5)+'/'
from t join @my my on t.obj=my.obj or t.attr=my.attr
where t.hy not like '%/'+str(my.obj,5)+right(space(4)+my.attr,5)+'/%'
),
p as(
select *,row_number() over(partition by gr order by lv desc) tp
from t
)
select * from p where tp=1
option(maxrecursion 0);