8

約50,000行を含むテーブル(dbo。[Message])にhierarchyIDを実装しようとしています(将来大幅に増加します)。ただし、約25件の結果を取得するには30〜40秒かかります。

ルートノードは一意性を提供するためのフィラーであるため、後続のすべての行はそのダミー行の子になります。

テーブルを深さ優先でトラバースできるようにする必要があり、hierarchyID列(dbo。[Message] .MessageID)をクラスタリングの主キーにし、計算されたsmallint(dbo。[Message] .Hierarchy)を追加しました。ノードのレベル。

使用法:.NetアプリケーションはhierarchyID値を介してデータベースに渡され、そのノードのすべての(存在する場合)子と親(フィラーであるため、ルートを除く)を取得できるようにしたいと考えています。

私が使用しているクエリの簡略化されたバージョン:

@MessageID hierarchyID   /* passed in from application */

SELECT 
m.MessageID, m.MessageComment 

FROM 
dbo.[Message] as m

WHERE 
m.Messageid.IsDescendantOf(@MessageID.GetAncestor((@MessageID.GetLevel()-1))) = 1

ORDER BY 
m.MessageID

私の理解では、インデックスはヒントなしで自動的に検出されるはずです。

フォーラムの検索から、幅優先のインデックスを扱うときにインデックスヒントを利用している人々を見てきましたが、深さ優先の状況ではこのアプリケーションを観察していません。それは私のシナリオに関連するアプローチでしょうか?

私は過去数日間、この問題の解決策を見つけようとしていましたが、役に立ちませんでした。助けていただければ幸いです。これが私の最初の投稿であるため、これが「noobish」の質問と見なされる場合は事前に謝罪します。MSのドキュメントを読み、数え切れないほどのフォーラムを検索しましたが、特定の問題。

4

2 に答える 2

9

深さ優先検索と幅優先検索のどちらを最適化しようとしているのかは完全には明らかではありません。質問は深さ優先を示唆していますが、最後のコメントは幅優先についてです。

深さ優先に必要なすべてのインデックスがあります(hierarchyid列にインデックスを付けるだけです)。幅優先探索では、計算列を作成levelするだけでは不十分であり、インデックスを作成する必要があります。

ALTER TABLE Message
ADD [Level] AS MessageID.GetLevel()

CREATE INDEX IX_Message_BreadthFirst
ON Message (Level, MessageID)
INCLUDE (...)

(非クラスター化インデックスの場合、おそらくINCLUDE-が必要になることに注意してください。そうでない場合、SQLServerは代わりにクラスター化インデックススキャンを実行する可能性があります。)

ここで、ノードのすべての祖先を検索しようとしている場合は、少し異なる方法を使用する必要があります。これらの検索を非常に高速に行うことができます。これは、hierarchyid各ノードにすべての祖先がすでに「含まれている」ためです。

これをできるだけ速くするためにCLR関数を使用しますが、再帰CTEを使用して実行できます。

CREATE FUNCTION dbo.GetAncestors
(
    @h hierarchyid
)
RETURNS TABLE
AS RETURN
WITH Hierarchy_CTE AS
(
    SELECT @h AS id

    UNION ALL

    SELECT h.id.GetAncestor(1)
    FROM Hierarchy_CTE h
    WHERE h.id <> hierarchyid::GetRoot()
)
SELECT id FROM Hierarchy_CTE

ここで、すべての祖先と子孫を取得するには、次のように使用します。

DECLARE @MessageID hierarchyID   /* passed in from application */

SELECT m.MessageID, m.MessageComment 
FROM Message as m
WHERE m.MessageId.IsDescendantOf(@MessageID) = 1
OR m.MessageId IN (SELECT id FROM dbo.GetAncestors(@MessageID.GetAncestor(1)))
ORDER BY m.MessageID

試してみてください-これでパフォーマンスの問題が解決するはずです。

于 2010-04-26T14:57:49.287 に答える
2

ここで回避策が見つかりました:http: //connect.microsoft.com/SQLServer/feedback/details/532406/performance-issue-with-hierarchyid-fun-isdescendantof-in-where-clause#

私はアプリケーションから渡されたものから始めたことを思い出してheirarchyIDください。私の目標は、その値のすべての親戚(祖先と子孫の両方)を取得することです。

私の特定の例では、SELECTステートメントの前に次の宣言を追加する必要がありました。

declare @topNode hierarchyid = (select @messageID.GetAncestor((@messageID.GetLevel()-1)))
declare @topNodeParent hierarchyid = (select @topNode.GetAncestor(1))
declare @leftNode hierarchyid= (select @topNodeParent.GetDescendant (null, @topNode))
declare @rightNode hierarchyid= (select @topNodeParent.GetDescendant (@topNode, null))

WHERE条項は次のように変更されました。

messageid.IsDescendantOf(@topNode)=1 AND (messageid > @leftNode ) AND (messageid < @rightNode )

クエリのパフォーマンスの向上は非常に重要です。

渡されたすべての結果について、シーク時間は平均20msになりました(120から420でした)。

25個の値をクエリする場合、以前は関連するすべてのノードを返すのに25〜35秒かかりました(各値に多くの親戚がある場合もあれば、ない場合もあります)。今ではたった2秒しかかかりません。

このサイトや他のサイトでこの問題に貢献してくれたすべての人に感謝します。

于 2010-04-26T20:58:57.590 に答える