消除查询中的聚簇索引扫描

Eliminate Clustered Index Scan in query

我有这个查询涉及 2 个连接的表。

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

当我 运行 它并获得执行计划时,我看到聚簇索引扫描 - 我想消除它并使用索引查找。

如何消除这里的聚簇索引扫描?我怎样才能构建一个新的索引?要为此添加更多上下文,此查询将由 12 个并行线程(12 个连接)调用,所以我需要这个快速和优化的。

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

https://www.brentozar.com/pastetheplan/?id=HkWjdvKDD

创建索引:

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

这个

  • 按您的两个固定值(QuoteNumber NOT NULL 和 IsDiscarded = 0)过滤
  • 按 LastUpdatedTicksSinceEpoch 对数据进行排序(当您将一个变量传递给 WHERE 子句中的那个变量时,它看起来最有可能成为您要排序的依据
  • 包括所有其他相关字段,因此不需要返回聚集索引(例如,成为覆盖索引)。

请注意,quote.ID 未包括在内,因为它是您的 PK(根据我在执行计划中所知道的),因此隐式包含在索引中。不过您可以包含它 - 完全没有害处(例如,索引大小和性能将完全相同)。

CREATE INDEX IX_q ON Quotes 
    (
    LastUpdatedTicksSinceEpoch
    )
INCLUDE
    (
    PolicyId,
    [Type],
    QuoteState,
    LatestFormData,
    QuoteNumber,
    IsDiscarded
    )
WHERE 
    (
    QuoteNumber is not null 
    AND IsDiscarded = 0
    )

注意:我认为这可能是您可以获得的最快速度,但您需要小心,因为 table 上的更多索引意味着更慢的插入、更新和删除。

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