用于查询Parent-Child表的所有祖先的递归CTE很慢

时间:2015-11-26 07:38:06

标签: sql-server performance common-table-expression hierarchical-data recursive-cte

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

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秒,执行计划为Execution Plan。查询返回给定Id

的5行

我知道我可以使用hierarchyid大大提高性能,我也知道有时使用while可能会更高效,但在这样的简单情况下,我希望看到更好的效果性能。 另请注意,我已经有ParentId

的索引

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

是否有调整可以大大改善这种性能?

2 个答案:

答案 0 :(得分:1)

好。事实证明这是缓慢的原因,修复比预期更有趣。

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作为所有类别的祖先,这对我们的使用没有任何影响。

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

答案 1 :(得分: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;