在SQL Server中存储不同类型的分层项的最佳方法

时间:2016-09-28 12:14:37

标签: sql-server hierarchy

我正在处理的项目有7个级别的业务层次结构。它们都不属于同一类型。这意味着,这不是组织结构图,所有项目都是某种级别的员工。他们是分部,地区,销售副总裁,业务部门等。是的,其中一些可能是员工,但不是全部。

目前,我在他们自己的表中各自遵循相似的模式,每个孩子都有一个父母的外键。所以从层次结构的最小部分开始:

BusinessUnit(表格)

ID
Name
AreaManagerID

AreaManager(表格)

ID
Name
RegionalManagerID

RegionalManager(表格)

ID
Name
DivisionID

分部(表)

ID
Name

还有3个表混合在一起,但这应该显示层次结构的每个级别之间相当简单的链接。每个孩子都必须有父母。不会有任何没有BusinessUnits的AreaManager。

在HierarchyID上读一点我不完全确定它会对我有所帮助。

我知道上面的作品,很好。但是当我被赋予一个部门并且需要找到其中的所有BU时,我更想知道是否有更好的方法和/或更快的方式。或者甚至被赋予一个区域并且需要找到其中的所有BU。

1 个答案:

答案 0 :(得分:0)

如果你正在寻找"给我后代",HierarchyID非常快速地找到任意深度的后代。如果你这样做,我会将所有实体放在一个表中,其中包含不同类型的分组表。看起来有点像这样:

CREATE TABLE [dbo].[BusinessEntity] (
    [EntityID] INT IDENTITY NOT NULL PRIMARY KEY,
    [ParentEntityID] INT
        REFERENCES [dbo].[BusinessEntity] ([EntityID]),
    [EntityType] TINYINT NOT NULL,
    [Path] HIERARCHYID
);

CREATE TABLE [dbo].[BusinessUnit] (
    [ID] INT NOT NULL PRIMARY KEY
        REFERENCES [dbo].[BusinessEntity] ([EntityID]),
    [Name] VARCHAR(255) NOT NULL,
    [AreaManagerID] INT NOT NULL
);

CREATE TABLE [dbo].[AreaManager] (
    [ID] INT NOT NULL PRIMARY KEY
        REFERENCES [dbo].[BusinessEntity] ([EntityID]),
    [Name] VARCHAR(255) NOT NULL,
    [RegionalManagerID] INT NOT NULL
);

CREATE TABLE [dbo].[RegionalManager] (
    [ID] INT NOT NULL PRIMARY KEY
        REFERENCES [dbo].[BusinessEntity] ([EntityID]),
    [Name] VARCHAR(255) NOT NULL,
    [DivisionID] INT NOT NULL
);

CREATE TABLE [dbo].[Division] (
    [ID] INT NOT NULL PRIMARY KEY
        REFERENCES [dbo].[BusinessEntity] ([EntityID]),
    [Name] VARCHAR(255)
);

当您要插入其中一个实际表格(例如BusinessUnit,RegionalManager等)时,您首先要在BusinessEntity中创建一条记录,然后使用生成的标识值作为插入的标识符。您还需要使路径列与层次结构中的关系保持同步。也就是说,让我们说我在BusinessEntity中有以下数据:

SET IDENTITY_INSERT [dbo].[BusinessEntity] ON;
INSERT INTO [dbo].[BusinessEntity]
        ( [EntityID],
          [ParentEntityID] ,
          [EntityType] 
        )
VALUES  
    (1, NULL, 1),
    (2, 1, 2),
    (3, 1, 2),
    (4, 2, 3),
    (5, 3, 3),
    (6, 4, 4),
    (7, 6, 5);

然后我可以使用以下CTE生成Path值

WITH cte AS (
    SELECT [be].[EntityID], [be].[ParentEntityID], CAST(CONCAT('/', [be].[EntityID], '/') AS VARCHAR(MAX)) AS [Path] 
    FROM [dbo].[BusinessEntity] AS [be]
    WHERE [be].[ParentEntityID] IS null

    UNION ALL

    SELECT [child].[EntityID], [child].[ParentEntityID], CAST(CONCAT([parent].[Path], child.[EntityID], '/') AS VARCHAR(MAX))
    FROM [dbo].[BusinessEntity] AS [child]
    JOIN [cte] AS [parent]
        ON [child].[ParentEntityID] = [parent].[EntityID]

)
UPDATE [be]
SET [be].[Path] = cte.[Path]
FROM [dbo].[BusinessEntity] AS be
JOIN cte
    ON [be].[EntityID] = [cte].[EntityID]
WHERE [Path] IS NULL;

当然,保持更新它们要容易得多。当您插入新行时,从父行抓取路径,将ID添加到其中,这就是您的路径。更新行的父级有点棘手,但并不可怕。我将它作为练习留给读者。但作为提示,它涉及HierarchyID数据类型的GetReparentedValue()方法。最后,如果你不相信Path中的值(因为它是派生值),你可以设置你不信任的任何值为NULL并重新运行上面的cte更新。