10 index sql-server optimization execution-plan sql-server-2012
我知道在使用索引或表扫描时,SQL Server 使用统计信息来查看哪个更好。
我有一个有 2000 万行的表。我在 (SnapshotKey, Measure) 和这个查询上有一个索引:
select Measure, SnapshotKey, MeasureBand
from t1
where Measure = 'FinanceFICOScore'
group by Measure, SnapshotKey, MeasureBand
Run Code Online (Sandbox Code Playgroud)
查询返回 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]
Run Code Online (Sandbox Code Playgroud)
表上没有PK,因为它是一个数据仓库。
CREATE NONCLUSTERED INDEX [nci_SnapshotKeyMeasure] ON [t1]
(
[SnapshotKey] ASC,
[Measure] ASC
)
Run Code Online (Sandbox Code Playgroud)
Aar*_*and 17
如果您返回许多行和/或行非常宽,索引查找可能不是最佳选择。如果您的索引未覆盖,查找可能会很昂贵。请参阅此处的 #2。
在您的场景中,查询优化器估计执行 50,000 次单独查找将比单次扫描更昂贵。优化器在扫描和查找(对查询所需的列进行 RID 查找,但不存在于非聚集索引中)之间的选择基于每个备选方案的估计成本。
优化器总是选择它考虑的最低成本的替代方案。如果查看两个执行计划根节点中的Estimated Subtree Cost属性,您会发现扫描计划的估计成本低于搜索计划。结果,优化器选择了扫描。这基本上就是你问题的答案。
现在,优化器使用的成本模型基于假设和“幻数”,这些假设和“幻数”不太可能与您的系统性能特征相匹配。特别是,模型中做出的一个假设是查询开始执行时内存中没有任何所需的数据或索引页。另一个是顺序 I/O(预期用于扫描)比 RID 查找假设的随机 I/O 模式便宜。还有许多其他这样的假设和警告,太多了,无法在此详细介绍。
尽管如此,成本模型作为一个整体已被证明可以为大多数查询、大多数数据库模式、大多数硬件配置、大多数时间、任何地方生成通常“足够好”的计划。仔细想想,这真是一项了不起的成就。
模型限制和其他因素有时意味着优化器选择的计划实际上根本“不够好”。您报告“性能良好”,因此这里似乎并非如此。
Rob*_*ley 10
您实际上有 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
Run Code Online (Sandbox Code Playgroud)
...你需要:
CREATE INDEX ixWhatever
ON t1 (Measure, SnapshotKey, MeasureBand)
INCLUDE (NumLoans,PrinBal);
Run Code Online (Sandbox Code Playgroud)
WHERE 条件中的字段不是索引的前导字段。
您已measure定义为 NVARCHAR,因此在文字前加上N: where Measure = N'FinanceFICOScore'。
考虑在 上创建聚集索引SnapshotKey。如果它是唯一的,那么它可以是一个 PK(和集群)。如果不是唯一的,则它不能是 PK,但仍可以是非唯一的聚集索引。那么你的非聚集索引将只在measure列上。
并且,考虑到 中的第一个字段GROUP BY也是measure,这也将从measure成为领先字段中受益。
实际上,对于此操作,您可能需要Measure, SnapshotKey, MeasureBand按照与GROUP BY子句匹配的确切顺序在上定义非聚集索引。MeasureBand由于非聚集索引已经基于Measure,并且MeasureKey已经包含在索引中,因为它现在是聚集索引键(不,Measure不会在非聚集索引中重复),因此才真正增加了大小。
@Rob 在对他的回答的现已删除的评论中提到,解决此问题只需要按此顺序使用这三个字段定义非聚集索引,SnapshotKey并且不需要在 上创建聚集(非唯一)索引。虽然他可能是正确的(我希望更少的字段可以工作),但我仍然认为拥有聚集索引不仅对这个操作有益,而且可能对大多数其他操作有益。
| 归档时间: |
|
| 查看次数: |
6846 次 |
| 最近记录: |