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 のModels for hierarchy dataなどの記事やプレゼンテーションを調べましたが、単一のノードに対する挿入クエリしかなく、そのようなツリーを作成するには永遠に時間がかかります。

ありがとう。

編集:
CategoryHierarchy テーブルの RelID は純粋に主キーのためのものであり、Category テーブルのノード ID には関係ありません。

また、クロージャーテーブルとは、次のようなテーブルを意味します。

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

最初の 2 つの列は複合主キーであり、Category.id に対する個別の外部キーです。

4

3 に答える 3

18

私は同じことを理解しようとしていましたが、再帰的な CTE でそれを望んでいました。これはあなた (SQL Server 2008+) にはうまくいきませんでしたが、他の人が探しているものは次のとおりです。

重要なのは、アンカーがルート ノード ( where 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 に答える