Sql 消除查询中的聚集索引扫描

Sql 消除查询中的聚集索引扫描,sql,sql-server,database,database-design,Sql,Sql Server,Database,Database Design,我有一个查询,涉及两个连接的表 Select q.id, q.LastUpdatedTicksSinceEpoch, q.[Type] [QuoteType], q.LatestFormData [FormDataJson], q.QuoteNumber, q

我有一个查询,涉及两个连接的表

Select  q.id, 
                         q.LastUpdatedTicksSinceEpoch,
                            q.[Type] [QuoteType],
       q.LatestFormData [FormDataJson], 
                            q.QuoteNumber, 
                            q.QuoteState,
       q.policyId,
                            p.CustomerId, 
                            p.CustomerFullName, 
                            p.CustomerAlternativeEmail,
                            p.CustomerHomePhone,
                            p.CustomerMobilePhone,
                            p.CustomerWorkPhone,
                            p.CustomerPreferredName,
       p.ProductId
                            from Quotes q
                    INNER JOIN  PolicyReadModels p on q.PolicyId=p.id
                    where
                            p.TenantId = @TenantId
                            and p.Environment = @Environment
                           and q.LastUpdatedTicksSinceEpoch > @LastUpdatedTicksSinceEpoch
         and q.QuoteState <> 'Nascent'
                            and q.QuoteNumber is not null
                            and q.IsDiscarded = 0
     ORDER BY q.LastUpdatedTicksSinceEpoch
选择q.id,
q、 自新纪元以来最新更新的tick,
q、 [Type][QuoteType],
q、 LatestFormData[FormDataJson],
q、 引述,
q、 房地产,
q、 policyId,
p、 客户ID,
p、 CustomerFullName,
p、 客户替代电子邮件,
p、 客户家庭电话,
p、 客户移动电话,
p、 客户工作电话,
p、 CustomerPreferredName,
p、 产品ID
引自q
q.PolicyId=p.id上的内部连接PolicyReadModels p
哪里
p、 TenantId=@TenantId
p.Environment=@Environment
和q.LastUpdatedTicksSinceEpoch>@LastUpdatedTicksSinceEpoch
和q.QuoteState“新生”
并且q.QuoteNumber不为null
q.IsDiscared=0
按q.LastUpdatedTicksSinceEpoch订购
当我运行它并获得执行计划时,我会看到聚集索引扫描——我想消除它并使用索引搜索

如何在此处消除聚集索引扫描?如何构造新索引?为了增加更多的上下文,这个查询将由12个并行线程(12个连接)调用,所以我需要这个快速优化的查询

这是我的查询和执行计划:

在以下内容上创建索引:

create index someIndex on
Quotes (LastUpdatedTicksSinceEpoch)
include (Id, Type, LatestFormData, QuoteNumber, QuoteState, PolicyId, IsDiscarded)
where IsDiscarded = 0
                      
这个

  • 按两个固定值筛选(QuoteNumber NOT NULL和IsDiscard=0)
  • 按LastUpdatedTicksSinceEpoch对数据进行排序(当您将一个变量传递给WHERE子句中的变量时,该变量似乎是最有可能排序的变量)
  • 包括所有其他相关字段,因此无需返回聚集索引(例如,成为覆盖索引)
请注意,quote.ID不包括在内,因为它是您的PK(从执行计划中我可以看出),因此隐式包含在索引中。您可以包括它,但不会造成任何伤害(例如,索引大小和性能将完全相同)

注意:我认为这可能是最快的,但您需要小心,因为表上的索引越多,插入、更新和删除的速度就越慢


我经常发现,可以在许多地方使用的更短的索引比这样一个非常具体的索引更可取(请记住,如果某些字段(如LatestFormData)很大,那么这个索引可能会变得臃肿,对其他查询没有那么大的用处)。

我得到了一个提升,因为您的答案基本相同(除非QuoteNumber有很多空值,我认为这不太可能),然后您首先回答。我发现
不是空的
筛选器并不总是处理得很好,所以我倾向于不经常使用它们。出于它的价值,我选择了这第一个作为我的答案,但当我运行它时,它花了很长时间才完成。添加此子句(QuoteNumber不是空的)实际上对我有效(跑了28秒)。所以我必须选择seanb的答案。是的,有一个很大的“视情况而定”关于选择性,以及它是否会在null上使用过滤器。我很高兴选择Sean的答案。谢谢seanb。如果行被更新,比如说一个单引号行在一个小时内被更新多达5次,这会不会不好。而且,是的,最新的FormData会变大(它的json值),并且它会被更新-我只是删除索引中最新的FormData吗?@seanbNote for the OP:索引有两种帮助方式。首先,覆盖索引包括您需要的所有字段,因此它们可以帮助您读取更少的数据。更重要的是,正确列上的索引允许引擎只查找适当的点。我强烈建议Brent Ozar的1到4或#5。一个索引和一个更新很少会成为问题。只有当你有很多索引,并且更新了数千行时,它才真正开始变得有趣(取决于硬件/内存等).Re大字段:如果索引中不包含此字段,则此查询不再包含在索引中,因此需要返回到聚集索引以获取数据。一种实用的方法:添加所需的查询(例如,此查询)直到你有太多,然后优化它们。为了对其他成员有用,你应该包括你的推理,为什么你认为这个查询可以优化,你尝试了什么没有起作用,例如,你实现了什么索引?对于Quotes表,只有PK。我将让这个查询在并行线程(12个连接)中运行。
CREATE INDEX IX_q ON Quotes 
    (
    LastUpdatedTicksSinceEpoch
    )
INCLUDE
    (
    PolicyId,
    [Type],
    QuoteState,
    LatestFormData,
    QuoteNumber,
    IsDiscarded
    )
WHERE 
    (
    QuoteNumber is not null 
    AND IsDiscarded = 0
    )