5

約70,000行と2列(両方VARCHAR(16))で構成されるテーブルがあります:idparent_id

特定のレコードが「ルート」ノードからどれだけ離れているかを示す「深さ」列にデータを入力したいと思います。

例えば

id,parent_id,depth
A,NULL,0
B,A,1
C,A,1
D,B,2
E,D,3

私は、同様の質問に対するこの回答に基づいてクエリを作成することから始めました。

WITH myCTE(id, depth) AS
(
    SELECT id, 0 FROM objects where id = 'A'
    UNION ALL
    SELECT objects.id, depth + 1 FROM myCTE JOIN objects ON objects.parent_id = myCTE.id
)
SELECT id, depth FROM myCTE

私のデータセット(〜80,000行)では、上記の実行にはほぼ2時間かかります!

次に、クエリをループとして記述し、パフォーマンスを大幅に向上させました。

ALTER TABLE objects ADD depth INT NULL
DECLARE @counter int
DECLARE @total int
SET @counter = 0
UPDATE objects SET depth = 0 WHERE id = 'A'

SELECT @total = COUNT(*) FROM objects WHERE depth IS NULL

WHILE (@total > 0)
BEGIN
    UPDATE objects SET depth = @counter + 1 WHERE parent_id IN (
        SELECT id FROM objects WHERE depth = @counter
    )
    SELECT @total = COUNT(*) FROM objects WHERE depth IS NULL
    SET @counter = @counter + 1
END

上記のコードは数分しかかかりません(そして、既存のテーブルに結果を追加するという利点があります)

私の質問は、私の結果がこの問題にCTEを使用する典型的なものであるかどうか、またはそれを説明する可能性のある見落としているものがあるかどうかです。インデックス、多分?(私は今テーブルに何もありません)

4

2 に答える 2

8

のインデックスが必要になりますparent_id。CTEの再帰部分は、常にネストされたループ結合を使用し、ヒントを結合しません(結果はスタックスプールに追加され、行はLIFOの順序で1つずつ処理されます

インデックスがないparent_id場合は、ネストされたループの内側でテーブルを複数回スキャンする必要があります。行数が増えると、パフォーマンスは指数関数的に低下します。

再帰のないクエリでは、再帰のレベルごとにテーブルを2回だけスキャンするさまざまな結合タイプ(ハッシュまたはマージ)を使用できます。この場合、ソートを回避する有用なインデックスがないため、ハッシュ結合である可能性があります。

于 2013-02-05T11:57:44.043 に答える
0

HierarchyIDデータ型の使用を検討しましたか?それはあなたの人生をとても楽にしてくれるでしょう。

CREATE TABLE Groups.tblHierarchyNode
(
        NodeID              Int IDENTITY (0,1),
        NodeHID             HierarchyID NOT NULL,   -- DB Hierarchy ID of where I am in a tree
        HierarchyLevel      AS NodeHID.GetLevel(),  -- Numerical level of where I am in tree
)

現在、これを多くの階層テーブルに使用しています。テーブルの数については少し賢くする必要がありますが、階層を上下に移動したり、祖先や子孫を取得したりするのと同様に、レポートは簡単です。

于 2013-02-05T16:04:42.133 に答える