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

It's not entirely clear whether you're trying to optimize for depth-first or breadth-first search; the question suggests depth-first, but the comments at the end are about breadth-first.

You have all the indexes you need for depth-first (just index the hierarchyid column). For breadth-first, it's not enough just to create the computed level column, you have to index it too:

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

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

(Note that for non-clustered indexes you'll most likely need the INCLUDE - otherwise, SQL Server may resort to doing a clustered index scan instead.)

Now, if you're trying to find all ancestors of a node, you want to take a slightly different tack. You can make these searches lightning-fast, because - and here's what's cool about hierarchyid - each node already "contains" all of its ancestors.

I use a CLR function to make this as fast as possible, but you can do it with a recursive 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

Now, to get all of the ancestors and descendants, use it like this:

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

Try it out - this should solve your performance problems.

于 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 )

查询性能提升非常显着:

对于传入的每个结果,寻道时间现在平均为 20 毫秒(从 120 到 420)。

查询 25 个值时,之前需要 25 - 35 秒才能返回所有相关节点(在某些情况下,每个值都有很多亲戚,在某些情况下没有)。现在只需 2 秒。

非常感谢所有在本网站和其他网站上为此问题做出贡献的人。

于 2010-04-26T20:58:57.590 回答