12

我有一个数据库,其中包含使用邻接列表模型存储的类别层次结构。

层次结构为 3 级深(不包括虚构的根节点),包含大约 1700 个节点。第 2 层和第 3 层的节点可以有多个父节点。一个附加表用于多对多关系,如下所示:

CREATE TABLE dbo.Category(
    id int IDENTITY(1,1) NOT NULL,
    name varchar(255) NOT NULL,
)

CREATE TABLE dbo.CategoryHierarchy(
    relId int IDENTITY(1,1) NOT NULL,
    catId int NOT NULL,
    parentId int NOT NULL,
)

如果我转而使用传递闭包表方法(为了数据完整性等),是否可以执行一个相对简单的查询来生成闭包表的值?(使用 SQL Server 2005)

我已经浏览了诸如 Bill Karwin 的分层数据模型之类的文章和演示文稿,但它只包含针对单个节点的插入查询,而且我要花很长时间才能创建这样的树。

谢谢。

编辑:
CategoryHierarchy 表中的 RelID 纯粹是为了主键,它与 Category 表的节点 ID 无关。

同样通过闭包表,我的意思是这样的表:

CREATE TABLE ClosureTable (
    ancestor int NOT NULL,
    descendant int NOT NULL,
    [length] int NOT NULL,
)

其中前两列是复合主键,并且分别是 Category.id 的外键。

4

3 回答 3

18

我试图弄清楚同样的事情,但希望它在递归 CTE 中。这对您不起作用(SQL Server 2008+),但这是我为其他人寻找的最终结果。

关键是锚点不是你的根节点(在哪里parent_id IS NULL),而是你所有的零深度行都在闭包表中。

桌子

CREATE TABLE dbo.category (
    id         INT IDENTITY(1, 1) NOT NULL,
    parent_id  INT                    NULL
)

数据

INSERT INTO dbo.category (id, parent_id)
VALUES
    (1, NULL),
    (2, 1),
    (3, 1),
    (4, 2)

CTE

WITH category_cte AS
(
    SELECT
        id AS ancestor,
        id AS descendant,
        0  AS depth
    FROM dbo.category

    UNION ALL

    SELECT
        CTE.ancestor  AS ancestor,
        C.id          AS descendant,
        CTE.depth + 1 AS depth
    FROM dbo.category AS C
    JOIN category_cte AS CTE
        ON C.parent_id = CTE.descendant
)
SELECT * FROM category_cte

结果

ancestor descendant depth
-------- ---------- -----
1        1          0     <- anchor query
2        2          0
3        3          0
4        4          0
2        4          1     <- first recursive query
1        2          1
1        3          1
1        4          2     <- second recursive query
于 2015-04-15T00:26:31.267 回答
5

我想我已经能够自己制定解决方案。

如果有人有更好的方法,请发表评论。

IF OBJECT_ID('dbo.ClosureTable', 'U') IS NOT NULL
    DROP TABLE dbo.ClosureTable
GO

CREATE TABLE dbo.ClosureTable (
    ancestor int NOT NULL,
    descendant int NOT NULL,
    distance int NULL
)
GO

DECLARE @depth INT
SET @depth = 1

INSERT INTO dbo.ClosureTable (ancestor, descendant, distance)
SELECT catid, catid, 0 FROM dbo.Category -- insert all the self-referencing nodes

WHILE (@depth < 4) -- my tree is only 4 levels deep, i.e 0 - 3
BEGIN
    INSERT INTO dbo.ClosureTable (ancestor, descendant, distance)
    SELECT ct.ancestor, h.catid, @depth
    FROM dbo.ClosureTable ct INNER JOIN dbo.CategoryHierarchy h ON ct.descendant = h.parentid
    WHERE ct.distance = @depth - 1

    SET @depth = @depth + 1
END

干杯:)

于 2012-09-27T16:38:04.593 回答
3
  WITH Children (id, name, iteration) AS (
    SELECT id, name, 0
    FROM Category 
    --WHERE id = @folderid -- if you want a startpoint
    UNION ALL 
    SELECT b.id, b.name, a.iteration + 1
    FROM Children AS a, Category AS b, CategoryHierarchy c
    WHERE a.id = c.parentId
      AND b.id = c.catId 
  )
  SELECT id, name, iteration FROM Children

未经测试,但应该像这样工作。至少开始如何在没有循环的情况下快速执行此操作。

编辑:我错过了,而不是 relId,它是必须链接到 Childrens 表的 parentId。但结果应该是一个包含所有表格的表格。所以这不是你最初想要的吗?

于 2012-09-27T13:12:07.370 回答