【问题标题】:How to avoid sorting results, returned by Index Seek?如何避免 Index Seek 返回的排序结果?
【发布时间】:2011-07-24 15:39:09
【问题描述】:

好吧,问题并不是真正“如何避免”排序,因为它是业务逻辑所要求的,而是如何使用索引初步完成,而不是在旅途中。

我有一个查询计划:

|--Sort(TOP 5, ORDER BY:([this_].[DateAdded] DESC))
   |--Nested Loops(Inner Join, OUTER REFERENCES:([this_].[Id], [Expr1002]) OPTIMIZED WITH UNORDERED PREFETCH)
        |--Index Seek(OBJECT:([storm].[dbo].[Items].[IX_Items_ByLocationSorted] AS [this_]), SEEK:([this_].[Status]=(1) AND [this_].[RegionId]=(32) AND [this_].[LocationId]=(32001)),  WHERE:([storm].[dbo].[Items].[SubcategoryTypeId] as [this_].[SubcategoryTypeId]=(88) AND ([storm].[dbo].[Items].[IsHidden] as [this_].[IsHidden]<(1) OR [storm].[dbo].[Items].[IsHidden] as [this_].[IsHidden]>(1))) ORDERED FORWARD)
        |--Clustered Index Seek(OBJECT:([storm].[dbo].[Items].[PK_Items] AS [this_]), SEEK:([this_].[Id]=[storm].[dbo].[Items].[Id] as [this_].[Id]) LOOKUP ORDERED FORWARD) 

您可以看到,Index Seek 操作返回一组行。比查询优化器执行键查找来检索整行(因为无论如何他都必须返回它),然后它按 DateAdded 列对所有这些行进行排序。这是一个明显且完全有效的行为。但考虑到 Index Seek(最大 30k)返回的行数,它真的很慢(可能需要长达 40 秒)。

如何加快查询速度并尽可能避免这种移动排序?

PS:查询的表大约有300万行,并且经常更新。这可能会导致页面锁定,但我认为这些锁定不会持续 40 秒。

查询:

SELECT TOP 5 * 
FROM Items 
WHERE 
     SubcategoryTypeId = 88 
     and RegionId = 32
 and LocationId = 32001
 and not (IsHidden = 1 and Status = 1) 
 ORDER BY DateAdded desc

【问题讨论】:

  • 当然,应该把它放在首位。我会把它添加到主帖中。
  • 当您的 查询 指定 ORDER BY DateAdded DESC 时,为什么要抱怨 DateAdded 上的排序?
  • 真正的问题不是如何避免排序本身,而是是否有任何方法可以不执行排序,而是依靠索引来存储已经排序的数据。

标签: sql-server sorting indexing lookup seek


【解决方案1】:

如果您愿意制作部分覆盖索引,您应该能够执行类似的操作(请注意,此代码基于我的表格,您必须对其进行修改以满足您的需要):

    select
    b.*
from
(
    select 
        IDkey,
        row_number() over (order by Years, Months) as z
    from dbo.tblWIN_LOSS
    where Won>=10000
) a
    inner join dbo.tblWIN_LOSS b
        on a.IDkey=b.IDkey
where a.z<=5

我不能 100% 确定这是否会给您带来性能提升,但我认为应该。

编辑:啊,你说 nhibernate 正在返回查询,所以我不知道你能做什么。

【讨论】:

    【解决方案2】:

    根据您的描述以及 SQL Server 如何处理索引,没有。

    排序是必要的步骤,SQL Server 数据没有“固有”顺序。索引数据根据索引的创建脚本进行排序的,但看起来无论如何您都会有办法。

    正如 gbn 正确指出的那样,覆盖索引会对您的表现有所帮助,但您在 cmets 中确定您对此不感兴趣,因此您可能处于无法获得的性能稳定期在不改变需求或数据结构的情况下关闭。

    【讨论】:

    • 这是一个有效点。我想重新排序是不可避免的。谢谢。
    【解决方案3】:

    您需要covering index 才能删除key lookup

    这将删除匹配 2 组数据所需的中间排序 (ORDERED FORWARD)(索引查找与 PK 查找相交)

    编辑:

    评论后:保持大索引(定义大?)或忍受缓慢的性能。您不能在索引不佳的情况下获得快速性能。这是一个二元选择。

    【讨论】:

    • 我真的不想删除这个键查找,因为表有很多行并且将它们全部添加到包含列只会使索引变得巨大。它可以帮助避免 Key Lookup,这当然是一件好事,但仅为此目的维护这个大索引的成本太高了。
    • 如果我可以对二元选择发表评论,我会 +2 :)
    • 如果我正确理解 SQL Server 索引的机制,它将与主表完全相同(以行为单位)(甚至,表包含不能包含的 [TEXT] 字段)在索引中)。此外,如果我们向表中添加新行,它会导致可维护性问题。我现在使用的索引覆盖了 where 条件,这是我可以允许查询的最大值,它返回所有表行。
    • @Rezgar - 您可以将这些列作为INCLUDEd 列,这意味着它们仅存在于叶级别,不用于在 b 树中查找索引值。您也可以不使用SELECT *,而只选择您需要的字段,然后包含这些字段。 SELECT * 本身就是一个非常糟糕的想法。
    • 即使列被包含在内,它们仍然是重复的,并且很难在更改时维护,即使在查找期间没有使用。至于 SELECT *,我非常同意你的观点。但是这个查询是由 NHibernate 生成的,无论如何我都需要返回的文本数据(最大的字段),因此在这种特定情况下手动指定列不会有太多好处。
    猜你喜欢
    • 2016-05-07
    • 1970-01-01
    • 1970-01-01
    • 2011-03-20
    • 1970-01-01
    • 1970-01-01
    • 2021-10-21
    • 2016-02-05
    • 2021-10-18
    相关资源
    最近更新 更多