我有一个数据库,其中包含使用邻接列表模型存储的类别层次结构。
层次结构深度为3级(不包括假想的根节点),包含大约1700个节点。第二级和第三级的节点可以有多个父节点。另外一个表用于多对多关系,如下所示:
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的Models for hierarchical data之类的文章和演示文稿,但只有单个节点的插入查询,我需要永远创建这样的树。
感谢。
编辑:
CategoryHierarchy表中的RelID纯粹是出于主键的缘故,它与Category表的节点ID无关。
同样通过闭包表,我的意思是这样一个表:
CREATE TABLE ClosureTable (
ancestor int NOT NULL,
descendant int NOT NULL,
[length] int NOT NULL,
)
前两列是复合主键,并且是Category.id的单独外键。
答案 0 :(得分:16)
我试图弄清楚同样的事情,但是想要它在一个递归的CTE中。这对你没有用(SQL Server 2008+),但这是我为其他任何人寻找的结果。
很难解释它是如何工作的,但关键是锚点不是你的根节点(parent_id IS NULL
),而是 all 你的零深度行在闭幕表中。
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)
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
2 2 0
3 3 0
4 4 0
2 4 1
1 2 1
1 3 1
1 4 2
答案 1 :(得分:4)
我想我自己能够找到解决方案。
如果有人有更好的方法,请发表评论。
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
干杯:)
答案 2 :(得分: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,是必须链接到儿童桌的parentId。但结果应该是包含所有表格的表格。那么这不是你原本想要的吗?