28

我想在 SQL Server 2008 中使用新的 HierarchyID 类型来处理小型 wiki 应用程序中的页面关系。但是,它需要有多个根节点,因为每个帐户的每个主要文章/页面都将是一个根节点。

从我所读到的 HierarchyID 类型只允许每列 1 个根节点这是正确的吗?有没有办法启用多个根节点?

4

6 回答 6

31

我一直在做一些测试,看来您不需要具有根层次结构标识的记录。

例如,通常您会有一个根节点(级别 1)和多个子节点,但您可以跳过根节点,没有根记录,只有从级别 2 开始的记录:

//table schema
CREATE TABLE [Entity](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Name] [varchar](50) NOT NULL
    [Hierarchy] [hierarchyid] NOT NULL,
 CONSTRAINT [PK_Entity] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

//Insert first 'root', which is technicall a child without a parent
INSERT INTO [Entity]
           ([Name]
           ,[Description]
           ,[Hierarchy])
     VALUES
           ('Root A'
           ,hierarchyid::GetRoot().GetDescendant(NULL,NULL))


//Create the second 'root'
INSERT INTO [Entity]
           ([Name]
           ,[Hierarchy])
     VALUES
           ('Root B'
           ,hierarchyid::GetRoot().GetDescendant((select MAX(hierarchy) from entity where hierarchy.GetAncestor(1) = hierarchyid::GetRoot()),NULL))

现在,如果您从表中选择所有行,您会看到:

SELECT [ID]
      ,[Name]
      ,[Hierarchy],
       [Hierarchy].ToString()
  FROM [Entity]

ID 名称层次结构(无列名)
1 根 A 0x58 /1/
2 根 B 0x68 /2/

我不确定这是否是推荐的做法,但从概念上讲,它允许您拥有多个根,只要您将树中的第二级视为根

于 2009-09-03T06:19:30.733 回答
11

我为制作唯一根节点所做的只是将您的表 PrimaryKey 作为 HierarchyId 投射到您想要的锚记录上,例如

给定一个具有 ArticleID | 的假表 ArticleID_Parent | 层次结构,您可以调整所有“根”,使其变得独一无二;

UPDATE [Article]
SET Hierarchy=CAST('/'+CAST([ArticleID] as varchar(30))+'/' AS hierarchyid)
WHERE [ArticleID_Parent]=0

..然后获取特定根的“分支”;

SELECT * FROM [Article]
WHERE Article.Hierarchy.IsDescendantOf((SELECT Hierarchy FROM Article WHERE ArticleID=XXXX)) = 1 
于 2010-09-07T20:09:25.293 回答
9

是的,您没看错——使用 HierarchyID 只允许一个根节点。就是这样,据我所知,除了引入一个人为的新“über-root”之外,除了让您拥有几个第一级“子根”之外别无他用。 ...

马克

更新:正如 Greg (@Greg0) 所指出的那样——这个答案实际上是不正确的——有关更多详细信息,请参阅他的答案。

于 2009-08-30T08:36:48.140 回答
7

可用于表示层次结构中位置的 hierarchyid 数据类型。但是,它本身并不强制执行层次结构。这是 MSDN 文档中针对hierarchyid的摘录。

由应用程序来生成和分配hierarchyid 值,以使行之间的所需关系反映在值中。

示例显示如何使用计算列和外键的组合来强制执行树。

CREATE TABLE Org_T3
(
   EmployeeId hierarchyid PRIMARY KEY,
   ParentId AS EmployeeId.GetAncestor(1) PERSISTED  
      REFERENCES Org_T3(EmployeeId),
   LastChild hierarchyid, 
   EmployeeName nvarchar(50)
)
GO

在您的情况下,您将修改计算列公式,以便为根记录返回 Null(SQL Server 中的 Null 值不强制执行外键)或记录的未修改的 hierarchyid(根将是它们自己的父级) .

这是上述示例的简化版本,它采用为根节点分配 null ParentId 的策略。

create table Node
(
    Id hierarchyid primary key,
    ParentId AS case when Id.GetLevel() = 1 then 
                    Null 
                else 
                    Id.GetAncestor(1) 
                end PERSISTED REFERENCES Node(Id),
    check (Id.GetLevel() != 0)
)

insert into Node (Id) values ('/1/');
insert into Node (Id) values ('/1/1/');
insert into Node (Id) values ('/'); --Fails as the roots will be at level 1.
insert into Node (Id) values ('/2/1/'); --Fails because the parent does not exist.

select Id.ToString(), ParentId.ToString() from Node;

只有上面的有效插入成功。

标识父标识

/1/ 空

/1/1/ /1/

于 2014-04-25T10:16:45.130 回答
6

是的,您可以有多个根。

对多个根没有数据库引擎限制。但是当然你在选择时需要一个鉴别器。考虑以下使用“Division”作为鉴别器的情况:

CREATE TABLE [EmployeeOrg](
    [OrgNode] [hierarchyid] NOT NULL,
    [OrgLevel]  AS ([OrgNode].[GetLevel]()),
    [EmployeeID] [int] NOT NULL,
    [Title] [varchar](20) NULL,
    [Division] [int] not null
) ON [PRIMARY]
GO


Insert into EmployeeOrg (OrgNode, EmployeeID, Title, Division) values ('/', 1, 'Partner A', 1 );
Insert into EmployeeOrg (OrgNode, EmployeeID, Title, Division) values ('/1/', 2, 'Part A Legal', 1 );
Insert into EmployeeOrg (OrgNode, EmployeeID, Title, Division) values ('/1/1/', 3, 'Part A Legal Asst', 1 );
Insert into EmployeeOrg (OrgNode, EmployeeID, Title, Division) values ('/', 4, 'Partner B', 2 );
Insert into EmployeeOrg (OrgNode, EmployeeID, Title, Division) values ('/1/', 5, 'Partner B Legal', 2 );
Insert into EmployeeOrg (OrgNode, EmployeeID, Title, Division) values ('/1/1/', 6, 'Partner B Legal Asst', 2 );

SELECT *  
FROM EmployeeOrg  
WHERE OrgNode.IsDescendantOf('/') = 1  and Division = 1

SELECT *  
FROM EmployeeOrg  
WHERE OrgNode.IsDescendantOf('/') = 1  and Division = 2 

这将按预期返回两个不同的层次结构。

于 2018-09-18T20:07:30.730 回答
2

难道你不能只有一个“不显示”的根目录,并且所有主要文章都在 1 级吗?

于 2009-08-30T19:40:46.160 回答