返回任何内容时,SQL Server查询运行速度较慢

时间:2015-01-14 19:17:34

标签: sql sql-server

结果集为空时,我的查询运行缓慢。当有东西要回来时,它会闪电般快。

    ;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子句,则查询会再次运行得非常快。所有这些表都被编入索引,查询优化器不建议任何改进。

您认为问题是什么以及如何通过空结果来改善查询的性能?

谢谢。

UPDATE1: 我正在添加执行计划。

http://www.filedropper.com/withorderby

http://www.filedropper.com/withoutorderby

UPDATE2: 我查看了oryol的建议并尝试将设施ID从树保存到表变量,并将其与设施表连接并按SortIndex排序。它消除了空结果的问题,但增加了查询的执行时间,结果集从250ms到950ms。

我还将查询更改为从facil中选择并加入到Facilities并添加了选项(强制订单)。结果与上述相同。

最后,我将非规范化的设施/类别映射表包含在此表中的SortIndex中。它将普通查询的执行时间从250ms略微增加到300ms,但它解决了空结果集问题。我猜,我会坚持这种方法。

1 个答案:

答案 0 :(得分:0)

首先 - 您可以将前两个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并通过以下方式为此表添加索引: NodeIdSortIndex

实际上,我们需要使用真实数据测试查询/索引。或者了解不同的数据统计数据:

  • 分类金额
  • 每个类别的设施数量和设施/类别映射表中的总行数
  • SortIndex发行版(它是唯一的吗?)