2

我需要在 SQL Server 2005 上实现一个多父树(或有向图)。我已经阅读了几篇文章,但其中大多数都使用具有唯一根的单父树,如下所示。

-My PC
   -Drive C
      -Documents and Settings
      -Program Files
         -Adobe
         -Microsoft
      -Folder X
   -Drive D
      -Folder Y
      -Folder Z

在这一个中,一切都来自一个根元素(我的电脑)。

就我而言,一个孩子可能有多个父母,如下所示:

G  A
 \ /
  B
 / \ 
X   C
  /  \
  D   E
  \ /
   F

所以我有以下代码:

create table #ObjectRelations
(
    Id varchar(20),
    NextId varchar(20)
)

insert into #ObjectRelations values ('G', 'B')
insert into #ObjectRelations values ('A', 'B') 
insert into #ObjectRelations values ('B', 'C')
insert into #ObjectRelations values ('B', 'X')
insert into #ObjectRelations values ('C', 'E') 
insert into #ObjectRelations values ('C', 'D') 
insert into #ObjectRelations values ('E', 'F') 
insert into #ObjectRelations values ('D', 'F') 

declare @id varchar(20)
set @id = 'A';

WITH Objects (Id, NextId) AS
( -- This is the 'Anchor' or starting point of the recursive query
  SELECT rel.Id,
         rel.NextId
    FROM #ObjectRelations rel
   WHERE rel.Id = @id
   UNION ALL -- This is the recursive portion of the query
  SELECT rel.Id,
         rel.NextId
    FROM #ObjectRelations rel
   INNER JOIN Objects -- Note the reference to CTE table name (Recursive Join)
      ON rel.Id = Objects.NextId
)
SELECT  o.*
FROM    Objects o

drop table #ObjectRelations

它返回以下 SET:

Id                   NextId
-------------------- --------------------
A                    B
B                    C
B                    X
C                    E
C                    D
D                    F
E                    F

预期结果集:

Id                   NextId
-------------------- --------------------
G                    B
A                    B
B                    C
B                    X
C                    E
C                    D
D                    F
E                    F

请注意,缺少 G->B 关系,因为它要求一个起始对象(这对我也不起作用,因为我从一开始就不知道根对象)并且使用 A 作为起点将忽略G->B 关系。

因此,此代码在我的情况下不起作用,因为它要求一个起始对象,这在单父树中很明显(将始终是根对象)。但是在多父树中,您可能有多个“根”对象(如在示例中,G 和 A 是“根”对象,其中根是没有父对象(祖先)的对象)。

所以我有点卡在这里......我需要修改查询以不要求起始对象并递归遍历整个树。我不知道 (Id, NextId) 实现是否有可能...可能我需要使用某种发生矩阵、邻接矩阵或其他任何东西将它存储为图形(请参阅http://willets.org/ sqlgraphs.html)。

有什么帮助吗?你们觉得怎么样?非常感谢您的宝贵时间 =)

干杯!

来源: 来源 1 来源 2 来源 3

4

3 回答 3

2

好吧,我终于想出了以下解决方案。这是我发现支持多根树和循环有向图的方式。

create table #ObjectRelations
(
    Id varchar(20),
    NextId varchar(20)
)

/* Cycle */
/*
insert into #ObjectRelations values ('A', 'B')
insert into #ObjectRelations values ('B', 'C') 
insert into #ObjectRelations values ('C', 'A')
*/

/* Multi root */

insert into #ObjectRelations values ('G', 'B')
insert into #ObjectRelations values ('A', 'B') 
insert into #ObjectRelations values ('B', 'C')
insert into #ObjectRelations values ('B', 'X')
insert into #ObjectRelations values ('C', 'E') 
insert into #ObjectRelations values ('C', 'D') 
insert into #ObjectRelations values ('E', 'F') 
insert into #ObjectRelations values ('D', 'F') 


declare @startIds table
(
    Id varchar(20) primary key
)

;WITH 
    Ids (Id) AS
    (
        SELECT  Id
        FROM    #ObjectRelations
    ),
    NextIds (Id) AS
    (
        SELECT  NextId
        FROM    #ObjectRelations
    )
INSERT INTO @startIds
/* This select will not return anything since there are not objects without predecessor, because it's a cyclic of course */
SELECT DISTINCT
    Ids.Id
FROM
    Ids
LEFT JOIN
    NextIds on Ids.Id = NextIds.Id
WHERE
    NextIds.Id IS NULL
UNION
/* So let's just pick anyone. (the way I will be getting the starting object for a cyclic doesn't matter for the regarding problem)*/
SELECT TOP 1 Id FROM Ids

;WITH Objects (Id, NextId, [Level], Way) AS
( -- This is the 'Anchor' or starting point of the recursive query
  SELECT rel.Id,
         rel.NextId,
         1,
         CAST(rel.Id as VARCHAR(MAX))
    FROM #ObjectRelations rel
   WHERE rel.Id IN (SELECT Id FROM @startIds)

   UNION ALL -- This is the recursive portion of the query

  SELECT rel.Id,
         rel.NextId,
         [Level] + 1,
         RecObjects.Way + ', ' + rel.Id
    FROM #ObjectRelations rel
   INNER JOIN Objects RecObjects -- Note the reference to CTE table name (Recursive Join)
      ON rel.Id = RecObjects.NextId
   WHERE RecObjects.Way NOT LIKE '%' + rel.Id + '%'

)
SELECT  DISTINCT 
        Id,
        NextId,
        [Level]
FROM    Objects
ORDER BY [Level]

drop table #ObjectRelations

可能对某人有用。这是给我的=P谢谢

于 2009-06-03T20:10:52.317 回答
1

如果要将所有根对象用作起始对象,则应首先更新数据以包含有关根对象(和叶子)的信息。您应该添加以下插入:

insert into #ObjectRelations values (NULL, 'G')
insert into #ObjectRelations values (NULL, 'A')
insert into #ObjectRelations values ('X', NULL)
insert into #ObjectRelations values ('F', NULL)

Id当然,您也可以编写锚查询,选择具有不作为 a 出现的 a的记录作为根节点NextId,但这更容易。

接下来,将您的锚查询修改为如下所示:

SELECT rel.Id,
       rel.NextId
FROM #ObjectRelations rel
WHERE rel.Id IS NULL

如果你运行这个查询,你会看到你得到了很多重复,很多弧线多次出现。这是因为您现在有两个来自锚查询的结果,因此树被遍历了两次。

这可以通过将您的 select 语句更改为此来解决(注意DISTINCT):

SELECT DISTINCT o.*
FROM   Objects o
于 2009-05-13T06:30:10.727 回答
0

如果你不想做 Ronald 建议的插入,这会做!

WITH CTE_MultiParent  (ID, ParentID) 
AS 
(
    SELECT ID, ParentID FROM #ObjectRelations
    WHERE ID NOT IN 
    (
        SELECT DISTINCT ParentID FROM #ObjectRelations
    )
    UNION ALL
    SELECT ObjR.ID, ObjR.ParentID FROM #ObjectRelations ObjR INNER JOIN CTE_MultiParent
    ON CTE_MultiParent.ParentID = ObjR.Id
)

SELECT DISTINCT * FROM CTE_MultiParent
于 2012-04-05T12:54:07.333 回答