SQL 未返回任何内容时服务器查询运行速度变慢
SQL Server query runs slower when nothing is returned
当结果集为空时,我的查询 运行 很慢。有事return,快如闪电。
;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
校长table是:
Facilities table 拥有设施,256k 条记录。
CategoryTree 用于在层次结构中对类别进行分组。
NodeId int,
CategoryId int,
ParentId int
FacilitiesCategoryTree 用于 link CategoryTree 到 Facilities。
给定 NodeId,第二个 CTE return 是给定节点的所有后代节点,包括其自身。然后是第三个 CTE,returns 属于这些节点的设施 ID。
最后,将最后一个 CTE 加入到实际设施中 table。结果按SortIndex排序,用于手动指示设施的顺序。
这个查询 运行 在需要 return 的情况下非常快,即使我包含更多的谓词,包括全文搜索和其他,但是当给定的分支没有任何设施时,这个查询大约需要。 2 秒到 运行.
如果我排除 order by 子句,查询 运行 再次非常快。所有这些 table 都已编入索引,查询优化器未提出任何改进建议。
您认为问题是什么?如何提高空结果查询的性能?
谢谢。
更新1:
我正在添加执行计划。
http://www.filedropper.com/withorderby
http://www.filedropper.com/withoutorderby
更新2:
我仔细阅读了 oryol 的建议,并尝试将设施 ID 从树保存到 table 变量并将其与设施 table 连接并按 SortIndex 排序。它消除了空结果的问题,但将结果集的查询执行时间从 250 毫秒增加到 950 毫秒。
我还将查询更改为 select,从 facil 和 join 到 Facilities 并添加了选项(强制顺序)。结果同上。
最后,我对 facility/category 映射 table 进行了非规范化,以便在此 table 中包含 SortIndex。它将普通查询的执行时间从 250 毫秒略微增加到 300 毫秒,但它解决了空结果集问题。我想,我会坚持这个方法。
第一件事 - 您可以将前两个 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
从类别树中查找设施中的一行。如果它存在 returns 结果行。如果没有 - 跳过此功能。
- 重复这些迭代直到返回 51 行
所以,在最坏的情况下(如果没有 51 个这样的设施,或者它们有很大 SortIndex
),将需要扫描所有 idx_Facilities_SortIndex
,这需要很多时间。
有多种方法可以解决此问题(包括提示优化器告知行数或连接顺序)以找到更好地处理真实数据库的最佳方法。可以尝试的第一个选项是将查询更改为:
- 将树中的设施 ID 保存到 table 变量
- 将其与设施 table 合并并按 SortIndex
排序
另一个选项(也可以与第一个选项结合使用)是尝试使用 FORCE ORDER
查询提示。在这种情况下,您需要将 select 语句从 facil
修改为 select 并将其加入 Facilities
并将 option (force order)
查询提示添加到末尾声明。
按 select 树中的所有设施进行无序查询。然后从 facilities table.
中提取其他 facility 字段
此外,了解树中设施的实际大小也很重要(根据执行计划中的估计,没有顺序,它真的很大 - 395982)。这个估计(或多或少)正确吗?
如果在使用类别树和 facility/categories 映射 table 连接后确实有大量设施返回,那么最好的解决方案是非规范化 facility/category 映射 table 在此 table 中包含 SortIndex 并通过 NodeId
和 SortIndex
.
向此 table 添加索引
所以实际上,我们需要用真实数据测试查询/索引。或了解不同的数据统计:
- 类别数量
- 每个类别的设施数量和设施/类别映射中的总行数table
- SortIndex 分布(是否唯一?)
- 等等
当结果集为空时,我的查询 运行 很慢。有事return,快如闪电。
;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
校长table是: Facilities table 拥有设施,256k 条记录。 CategoryTree 用于在层次结构中对类别进行分组。
NodeId int,
CategoryId int,
ParentId int
FacilitiesCategoryTree 用于 link CategoryTree 到 Facilities。
给定 NodeId,第二个 CTE return 是给定节点的所有后代节点,包括其自身。然后是第三个 CTE,returns 属于这些节点的设施 ID。
最后,将最后一个 CTE 加入到实际设施中 table。结果按SortIndex排序,用于手动指示设施的顺序。
这个查询 运行 在需要 return 的情况下非常快,即使我包含更多的谓词,包括全文搜索和其他,但是当给定的分支没有任何设施时,这个查询大约需要。 2 秒到 运行.
如果我排除 order by 子句,查询 运行 再次非常快。所有这些 table 都已编入索引,查询优化器未提出任何改进建议。
您认为问题是什么?如何提高空结果查询的性能?
谢谢。
更新1: 我正在添加执行计划。
http://www.filedropper.com/withorderby
http://www.filedropper.com/withoutorderby
更新2: 我仔细阅读了 oryol 的建议,并尝试将设施 ID 从树保存到 table 变量并将其与设施 table 连接并按 SortIndex 排序。它消除了空结果的问题,但将结果集的查询执行时间从 250 毫秒增加到 950 毫秒。
我还将查询更改为 select,从 facil 和 join 到 Facilities 并添加了选项(强制顺序)。结果同上。
最后,我对 facility/category 映射 table 进行了非规范化,以便在此 table 中包含 SortIndex。它将普通查询的执行时间从 250 毫秒略微增加到 300 毫秒,但它解决了空结果集问题。我想,我会坚持这个方法。
第一件事 - 您可以将前两个 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
从类别树中查找设施中的一行。如果它存在 returns 结果行。如果没有 - 跳过此功能。 - 重复这些迭代直到返回 51 行
所以,在最坏的情况下(如果没有 51 个这样的设施,或者它们有很大 SortIndex
),将需要扫描所有 idx_Facilities_SortIndex
,这需要很多时间。
有多种方法可以解决此问题(包括提示优化器告知行数或连接顺序)以找到更好地处理真实数据库的最佳方法。可以尝试的第一个选项是将查询更改为:
- 将树中的设施 ID 保存到 table 变量
- 将其与设施 table 合并并按 SortIndex 排序
另一个选项(也可以与第一个选项结合使用)是尝试使用 FORCE ORDER
查询提示。在这种情况下,您需要将 select 语句从 facil
修改为 select 并将其加入 Facilities
并将 option (force order)
查询提示添加到末尾声明。
按 select 树中的所有设施进行无序查询。然后从 facilities table.
中提取其他 facility 字段此外,了解树中设施的实际大小也很重要(根据执行计划中的估计,没有顺序,它真的很大 - 395982)。这个估计(或多或少)正确吗?
如果在使用类别树和 facility/categories 映射 table 连接后确实有大量设施返回,那么最好的解决方案是非规范化 facility/category 映射 table 在此 table 中包含 SortIndex 并通过 NodeId
和 SortIndex
.
所以实际上,我们需要用真实数据测试查询/索引。或了解不同的数据统计:
- 类别数量
- 每个类别的设施数量和设施/类别映射中的总行数table
- SortIndex 分布(是否唯一?)
- 等等