没有返回任何内容时,SQL Server 查询运行速度较慢
Posted
技术标签:
【中文标题】没有返回任何内容时,SQL Server 查询运行速度较慢【英文标题】:SQL Server query runs slower when nothing is returned 【发布时间】:2015-01-14 19:17:34 【问题描述】:当结果集为空时,我的查询运行缓慢。有东西要还,快如闪电。
;with tree(NodeId,CategoryId,ParentId) as (
select ct.NodeId, ct.CategoryId, ct.ParentId
from dbo.CategoryTree as ct
where ct.ParentId = 6
union all
select t.NodeId, t.CategoryId, t.ParentId from dbo.CategoryTree as t
inner join tree as t2 on t.ParentId = t2.NodeId
), branch(NodeId,CategoryId,ParentId) as
(
select NodeId, CategoryId, ParentId from dbo.CategoryTree as t
where t.NodeId = 6
union all
select NodeId, CategoryId, ParentId
from tree as t
),facil(FacilityId) as(
select distinct fct.FacilityId
from dbo.FacilitiesCategoryTree as fct
inner join branch b on b.NodeId = fct.CategoryNodeId
)
select top 51 f.Id, f.CityId, f.NameGEO,
f.NameENG, f.NameRUS, f.DescrGEO, f.DescrENG,
f.DescrRUS, f.MoneyMin, f.MoneyAvg, f.Lat, f.Lng, f.SortIndex,
f.FrontImgUrl from dbo.Facilities f
inner join facil t2 on t2.FacilityId = f.Id
and f.EnabledUntil > 'Jan 14 2015 10:23PM'
order by f.SortIndex
主要表是: 设施表保存设施,256k 记录。 CategoryTree 用于对层次结构中的类别进行分组。
NodeId int,
CategoryId int,
ParentId int
FacilitiesCategoryTree 用于将 CategoryTree 链接到设施。
给定 NodeId,第二个 CTE 返回给定节点的所有后代节点,包括它自己。然后是第三个 CTE,它返回属于这些节点的设施 ID。
最后,最后一个 CTE 加入到实际设施表中。结果由 SortIndex 排序,用于手动指示设施的顺序。
即使我包含更多谓词(包括全文搜索和其他),当有要返回的内容时,此查询也会运行得非常快,但是当给定分支没有任何设施时,此查询大约需要 .运行 2 秒。
如果我排除 order by 子句,查询再次运行得非常快。所有这些表都已编入索引,查询优化器并未提出任何改进建议。
您认为问题出在哪里,可以采取哪些措施来提高空结果查询的性能?
谢谢。
更新1: 我正在添加执行计划。
http://www.filedropper.com/withorderby
http://www.filedropper.com/withoutorderby
更新 2: 我查看了 oryol 的建议,并尝试将设施 ID 从树保存到表变量中,并将其与设施表连接起来,并按 SortIndex 排序。它消除了空结果的问题,但将结果集的查询的执行时间从 250 毫秒增加到了 950 毫秒。
我还将查询更改为从设施中选择并加入设施并添加选项(强制订单)。结果和上面一样。
最后,我对设施/类别映射表进行了非规范化,以在该表中包含 SortIndex。它将普通查询的执行时间从 250 毫秒略微增加到 300 毫秒,但它解决了空结果集的问题。我想,我会坚持这个方法。
【问题讨论】:
ORDER BY
一定给查询优化器提供了错误的提示,比如使用了低效的索引。您可以发布带有和不带有ORDER BY
的执行计划吗?
我在更新后的帖子中添加了两个执行计划。
【参考方案1】:
第一件事 - 您可以将前两个 CTE 稍微简化为一个:
with tree(NodeId,CategoryId,ParentId) as (
select ct.NodeId, ct.CategoryId, ct.ParentId
from dbo.CategoryTree as ct
where ct.NodeId = 6
union all
select t.NodeId, t.CategoryId, t.ParentId from dbo.CategoryTree as t
inner join tree as t2 on t.ParentId = t2.NodeId
)
优化器不知道或错误估计将为您的类别返回的设施数量的主要问题。因为您需要SortIndex
订购的设施,优化器决定:
SortIndex
订购的所有设施(使用适当的索引)
跳过未被其他过滤器覆盖的行 (EnabledUntil
)
使用给定的Facility Id
从类别树中查找设施中的一行。如果存在则返回结果行。如果没有 - 跳过这个设施。
重复这些迭代,直到返回 51 行
因此,在最坏的情况下(如果没有 51 个这样的设施或者它们有非常大的SortIndex
)将需要扫描所有idx_Facilities_SortIndex
,并且需要很多时间。
有几种方法可以解决此问题(包括提示优化器以告知行数或连接顺序)以找到更好地使用真实数据库的最佳方法。可以尝试的第一个选项是将查询更改为:
将设施 ID 从树保存到表变量 将其与设施表连接并按 SortIndex 排序另一个选项(也可以与第一个选项一起使用)是尝试使用FORCE ORDER
查询提示。在这种情况下,您需要修改您的 select 语句以从 facil
中进行选择,并将其连接到 Facilities
并将 option (force order)
查询提示添加到语句的末尾。
通过从树中选择所有设施进行无顺序查询。然后从设施表中提取其他设施字段。
此外,了解树中设施的实际大小也很重要(根据执行计划中的估计,没有顺序,它真的很大 - 395982)。这个估计(或多或少)正确吗?
如果您在加入类别树和设施/类别映射表后确实返回了大量设施,那么最好的解决方案是对设施/类别映射表进行非规范化以在该表中包含 SortIndex 并通过以下方式向该表添加索引NodeId
和 SortIndex
。
所以实际上,我们需要使用真实数据来测试查询/索引。或者了解不同的数据统计:
类别数量 每个类别的设施数量和设施/类别映射表中的总行数 SortIndex 分布(是否唯一?) 等【讨论】:
以上是关于没有返回任何内容时,SQL Server 查询运行速度较慢的主要内容,如果未能解决你的问题,请参考以下文章
SqlDataAdapter.Fill 方法不会给出任何错误,但也不会返回任何数据以用于长时间运行的查询 ado.net core SQL Server