2010-04-26 70 views
8

我試圖在包含大約50,000行的表(dbo。[Message])中實現hierarchyID(未來會大幅增長)。但是,需要30-40秒才能檢索到約25個結果。關於SQL Server HierarchyID深度優先性能的問題

爲了提供唯一性,根節點是填充符,因此每個後續行都是該虛擬行的子節點。

我需要能夠遍歷表深度優先,並已取得了HIERARCHYID柱(DBO。[信息] .MessageID)聚類主鍵,還添加了一個計算SMALLINT(DBO。[信息] .Hierarchy ),它存儲節點的級別。

用法:.Net應用程序將hierarchyID值傳遞到數據庫中,我希望能夠檢索該節點的所有(如果有的話)子節點和父節點(除root之外,因爲它是填充符)。

我使用的查詢的簡化版本:

@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 

據我瞭解,該指數應自動無提示檢測。

從搜索論壇我看到人們在處理廣度優先索引時使用索引提示,但沒有在深度優先的情況下觀察到這個應用。對我的情況來說,這是一種相關的方法嗎?

我花了這幾天試圖找到解決這個問題,但無濟於事。 我非常感謝任何幫助,因爲這是我的第一篇文章,如果這被認爲是一個'不好的'問題,我會提前道歉,我已經閱讀了MS文檔並搜索了無數論壇,但沒有遇到簡明的描述的具體問題。

+0

順便說一句,你有的查詢?正如所寫的,它總是在整個表中選擇所有節點。 @ MessageID.GetAncestor(@ MessageID.GetLevel() - 1)'把它一直帶到根,然後你選擇所有的後裔,這就是......一切。這就是爲什麼它如此緩慢。 – Aaronaught 2010-04-26 15:01:37

+0

只是爲了澄清:我的情況需要使用深度優先索引,對於混淆 (我指的是廣度優先,最後只是提供一個人們建議使用索引提示的例子) – ObjectiveCat 2010-04-26 15:03:13

回答

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秒。

非常感謝所有對本網站和其他人都有所貢獻的人。

8

目前尚不完全清楚您是否嘗試針對深度優先搜索或廣度優先搜索進行優化;這個問題表明深度優先,但最後的評論是關於寬度優先的。

您有深度優先所需的所有索引(僅索引hierarchyid列)。對於廣度優先,這不是濫竽充數創建的計算level列,你必須索引太:

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

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

(請注意,對於非聚集索引,你很可能需要INCLUDE - 否則,SQL Server可能會採取聚簇索引掃描來代替)。

現在,如果您嘗試查找節點的所有祖先,您希望採取稍微不同的方法。您可以快速進行這些搜索,因爲 - 以下是關於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 

試一下 - 這應該可以解決你的性能問題。

+0

對不起困惑,深度優先是我的追求! 非常感謝您的建議,我會馬上嘗試。 – ObjectiveCat 2010-04-26 15:00:57

+0

只是爲了測試目的,我已刪除@ MessageID.GetAncestor共 只留下: m.MessageId.IsDescendantOf(@MessageID)=在1 WHERE子句,當我跑了PROC,尋道時間依然是150間到每420ms結果 ,這對我的應用程序來說非常慢。 性能是一個優先事項,我對CLR完全不熟悉,但是我真的很想學習如何實現它,如果那將提供最佳性能。任何建議從哪裏開始? – ObjectiveCat 2010-04-26 15:34:08

+0

@AndalusianCat:CLR版本用於祖先查詢。如果你發現使用'IsDescendantOf'慢,請發佈一個實際的查詢,一個表模式(包括索引)和執行計劃。 'hierarchyid'查詢通常比這更快。 – Aaronaught 2010-04-26 18:41:37