0

我们有一个像这样的自引用表

CREATE TABLE Categories(
Id int IDENTITY(1,1) NOT NULL,
Title nvarchar(200) NOT NULL,
ParentId int NULL,
CONSTRAINT PK_Structures PRIMARY KEY CLUSTERED 
(
    Id ASC
)
CREATE NONCLUSTERED INDEX IX_Structures_ParentId ON Categories
(
    ParentId ASC
)

以及获取所有祖先的递归 cte:

Create View Ancestors
as
with A(Id, ParentId) as 
(
    select Id, Id from Categories 
    union all
    select e.Id, p.ParentId from Categories e 
    join A p on e.ParentId = p.Id
)
select * from A

现在我们查询给定类别的所有祖先,例如:

select * from Ancestors where Id = 1234

仅包含 100000 个类别的表需要 11 秒,执行计划为执行计划. 查询返回给定的 5 行Id

我知道我可以通过 using 大大提高性能hierarchyid,我也知道有时 usingwhile可以提高性能,但在这样一个简单的情况下,我希望看到更好的性能。另外,请注意我已经有一个索引ParentId

(图片显示的表格是问题中提到structure的表格的实际名称。Category

是否有调整可以大大提高此性能?

4

2 回答 2

2

好。事实证明缓慢的原因,并且修复比预期的要有趣得多。

Sql server 根据它们的定义而不是它们可能具有的语义来优化查询。有问题的视图从所有类别开始,并通过从 CTE 本身及其子项中查找元素来添加新行。现在要查找其中某行作为子项出现的所有行的方法,您需要计算整个查询然后将其过滤掉。只有人类读者理解查询计算任何类别的所有后代,当然也有任何类别的所有祖先。然后你知道你可以从底层开始递归地找到父母。这在查询定义中并不明显,仅从其语义含义来看。

如下重写视图将使其更快:

Create View Ancestors
as
with A(Id, ParentId) as 
(
    select Id, Id from Categories 
    union all
    select p.Id, e.ParentId from Categories e 
    join A p on e.Id = p.ParentId
)
select * from A

此视图创建的结果与所讨论的视图几乎相同。唯一的区别是它还显示 null 作为所有类别的祖先,这对我们的使用没有任何影响。

这个视图从底部开始构建层次结构并向上,这与我们打算查询它的方式兼容。

于 2016-01-14T12:33:27.357 回答
0

如果将过滤条件放在 CTE 中,执行计划会是什么样子?

with A(Id, ParentId) as 
(
    select Id, Id 
    from Categories 
    WHERE Categories.ID = 1234

    union all

    select e.Id, p.ParentId 
    from Categories e 
    join A p on e.ParentId = p.Id
)
select * 
from A;
于 2015-11-26T09:33:00.497 回答