我想在 SQL Server 2008 中使用新的 HierarchyID 类型来处理小型 wiki 应用程序中的页面关系。但是,它需要有多个根节点,因为每个帐户的每个主要文章/页面都将是一个根节点。
从我所读到的 HierarchyID 类型只允许每列 1 个根节点这是正确的吗?有没有办法启用多个根节点?
我想在 SQL Server 2008 中使用新的 HierarchyID 类型来处理小型 wiki 应用程序中的页面关系。但是,它需要有多个根节点,因为每个帐户的每个主要文章/页面都将是一个根节点。
从我所读到的 HierarchyID 类型只允许每列 1 个根节点这是正确的吗?有没有办法启用多个根节点?
我一直在做一些测试,看来您不需要具有根层次结构标识的记录。
例如,通常您会有一个根节点(级别 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/
我不确定这是否是推荐的做法,但从概念上讲,它允许您拥有多个根,只要您将树中的第二级视为根
我为制作唯一根节点所做的只是将您的表 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
是的,您没看错——使用 HierarchyID 只允许一个根节点。就是这样,据我所知,除了引入一个人为的新“über-root”之外,除了让您拥有几个第一级“子根”之外别无他用。 ...
马克
更新:正如 Greg (@Greg0) 所指出的那样——这个答案实际上是不正确的——有关更多详细信息,请参阅他的答案。
可用于表示层次结构中位置的 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/
是的,您可以有多个根。
对多个根没有数据库引擎限制。但是当然你在选择时需要一个鉴别器。考虑以下使用“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
这将按预期返回两个不同的层次结构。
难道你不能只有一个“不显示”的根目录,并且所有主要文章都在 1 级吗?