我们有一个像这样的自引用表
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秒,执行计划为。查询返回给定Id
我知道我可以使用hierarchyid
大大提高性能,我也知道有时使用while
可能会更高效,但在这样的简单情况下,我希望看到更好的效果性能。
另请注意,我已经有ParentId
(图片显示的是structure
表,它是问题中提到的Category
表的实际名称。
是否有调整可以大大改善这种性能?
答案 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;