执行计划未使用INDEX,而是使用表扫描


9

我知道使用索引或表扫描时,SQL Server使用统计信息来查看哪个更好。

我有一个2000万行的表。我在(SnapshotKey,Measure)上有一个索引,并且此查询:

select Measure, SnapshotKey, MeasureBand
from t1
where Measure = 'FinanceFICOScore'
group by Measure, SnapshotKey, MeasureBand

查询返回500k行。因此,查询仅选择表的2.5%的行。

问题是为什么SQL Server不使用我拥有的非聚集索引,而是使用表扫描?

统计信息已更新。

值得一提的是查询性能还是不错的。

表扫描

表扫描

强制索引

力指数

表/索引结构

CREATE TABLE [t1](
    [SnapshotKey] [int] NOT NULL,
    [SnapshotDt] [date] NOT NULL,
    [Measure] [nvarchar](30) NOT NULL,
    [MeasureBand] [nvarchar](30) NOT NULL,
    -- and many more fields
) ON [PRIMARY]

桌上没有PK,因为它是数据仓库。

CREATE NONCLUSTERED INDEX [nci_SnapshotKeyMeasure] ON [t1]
(
    [SnapshotKey] ASC,
    [Measure] ASC
)

Answers:


16

如果返回许多行和/或行很宽,则索引查找可能不是最佳选择。如果索引不覆盖,查找可能会很昂贵。请参阅此处的#2

在您的方案中,查询优化器估计执行50,000个单独的查找将比单次扫描更昂贵。优化程序在扫描和查找之间进行选择(使用RID查找查询所需的列,但不包含在非聚集索引中)是基于每个替代方法的估计成本

优化器总是选择其考虑的最低成本的替代方案。如果您查看两个执行计划的根节点中的“ 估计子树成本”属性,您会发现扫描计划的估计成本低于查找计划。结果,优化程序选择了扫描。从本质上讲,这就是您的问题的答案。

现在,优化器使用的成本模型基于假设和“魔术数字”,这些假设和“魔术数字”不太可能与您系统的性能特征相匹配。特别是,模型中的一个假设是查询开始执行时内存中没有所需的数据或索引页。另一个是顺序I / O(预期用于扫描)比为RID查找假定的随机I / O模式便宜。还有许多其他这样的假设和警告,在此不做详细介绍。

尽管如此,成本模型作为一个整体已经显示出,对于大多数查询,大多数数据库模式,大多数硬件配置,大多数时间,在任何地方,总的来说,它们都会产生“足够好的”计划。如果您考虑一下,那确实是一项成就。

模型限制和其他因素有时会意味着优化器选择的计划实际上根本不够“好”。您报告“性能很好”,因此这里似乎并非如此。


9

实际上,您有595,947个匹配行,大约占数据的3%。因此,查找成本很快就增加了。假设您的表中每页有100行,那么在表扫描中要读取200,000页。这比进行595,947次查询便宜得多。

使用问题中的GROUP BY子句,我认为使用复合键(Measure,SnapshotKey,MeasureBand)会更好。

查看“缺少索引”建议。它告诉您包括列以避免查找。更一般而言,如果您在查询中引用其他列,则它们将需要INCLUDE位于新索引的键或子句中。否则,它仍然需要进行595,947查找来获得这些值。

例如,对于查询:

select Measure, SnapshotKey, MeasureBand, SUM(NumLoans), SUM(PrinBal)
from t1
where Measure = 'FinanceFICOScore'
group by Measure, SnapshotKey, MeasureBand

...您需要:

CREATE INDEX ixWhatever 
ON t1 (Measure, SnapshotKey, MeasureBand) 
INCLUDE (NumLoans,PrinBal);

6
  1. WHERE条件中的字段不是索引的开头字段。

  2. 您已将measureNVARCHAR定义为NVARCHAR,因此在文字前加上一个Nwhere Measure = N'FinanceFICOScore'

考虑在上创建聚簇索引SnapshotKey。如果它是唯一的,那么它可以是PK(和群集)。如果不是唯一的,则它不能是PK,但仍然可以是非唯一的聚集索引。然后,您的非聚集索引将仅在该measure列上。

并且,考虑到的第一个领域GROUP BY也是measure,这也将受益于measure成为领先领域。

实际上,对于此操作,您可能需要改为Measure, SnapshotKey, MeasureBand按与GROUP BY子句匹配的确切顺序在上定义NonClustered Index 。从大小角度来看,这只是真正添加的内容,MeasureBand因为NonClustered索引已经基于Measure,并且MeasureKey已经包含在索引中,因为它现在是Clustered Index键(不,Measure不会在NonClustered索引中重复)。

@Rob曾在他的回答一个现在已经删除评论指出,解决这一问题只要求非聚集索引按次序用这三个字段定义,并创建一个集群(非唯一)指数SnapshotKey是不是必需的。尽管他可能是正确的(我希望可以使用更少的字段),但我仍然认为拥有聚集索引不仅对这项操作有好处,而且可能对大多数其他工作都有好处。


有关此答案的讨论已转移到聊天室
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.