2010-04-26 51 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