SQL 2008 HierarchyID с несколькими корневыми узлами - PullRequest
27 голосов
/ 30 августа 2009

Я хотел использовать новый тип HierarchyID в SQL Server 2008 для обработки отношений страниц в небольшом вики-приложении. Однако необходимо иметь несколько корневых узлов, поскольку каждая основная статья / страница для каждой учетной записи будет корневым узлом.

Из того, что я прочитал, тип HierarchyID разрешает только 1 корневой узел на столбец, это правильно? и есть ли способ включить несколько корневых узлов?

Ответы [ 6 ]

29 голосов
/ 03 сентября 2009

Я провел некоторое тестирование, и, похоже, вам не нужна запись с корневым иерархическим узлом.

Например, обычно вам нужен один корневой узел (уровень 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 Root A 0x58 / 1 /
2 Root B 0x68 / 2 /

Я не уверен, будет ли это рекомендуемой практикой, но концептуально это позволяет вам иметь несколько корней, если вы считаете 2-й уровень в дереве корнем

9 голосов
/ 08 сентября 2010

Чтобы создать уникальные корневые узлы, просто приведите таблицу 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 
8 голосов
/ 30 августа 2009

Да, вы правильно читаете - использование HierarchyID позволяет использовать только один корневой узел. Так оно и есть, и, насколько я знаю, никакого пути к этому нет, если не считать введения искусственного нового «über-корня», который не служит никакой другой цели, кроме как позволить вам иметь несколько «подкорней» первого уровня. ...

Марк

Обновление: , как указал Грег (@ Greg0) - этот ответ на самом деле неправильный - см. Его ответ для более подробной информации.

6 голосов
/ 25 апреля 2014

Тип данных иерархии, который можно использовать для представления позиции в иерархии. Это по своей сути не обеспечивает иерархии. Это выдержка из документации MSDN для ierarchyid .

Приложение должно генерировать и назначать значения иерархии таким образом, чтобы в значениях отражалась желаемая связь между строками.

В этом примере показано, как комбинация вычисляемого столбца и внешнего ключа может использоваться для принудительного применения дерева.

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 (внешние ключи не применяются для значений Null в SQL Server) или, возможно, неизмененный иерархический элемент записи (корни были бы их собственными родителями) будет возвращен.

Это упрощенная версия вышеприведенного примера, которая соответствует стратегии присвоения корневым узлам нулевого 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;

Успешны только допустимые вставки сверху.

Id ParentId

/ 1 / NULL

/ 1/1 / / 1 /

3 голосов
/ 18 сентября 2018

Да, у вас может быть несколько корней.

Нет ограничений на ядро ​​базы данных для нескольких корней. Но, конечно, вам нужен дискриминатор при выборе. Рассмотрим следующее, которое использует «Деление» в качестве дискриминатора:

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 

Возвращает две разные иерархии, как и ожидалось.

2 голосов
/ 30 августа 2009

разве у вас не может быть одного «не отображаемого» корня и иметь все основные статьи на уровне 1?

...