为什么SQL Server会忽略索引?


16

我有一个表,CustPassMaster其中有16列,其中一个是CustNum varchar(8),并且创建了一个index IX_dbo_CustPassMaster_CustNum。当我运行SELECT语句时:

SELECT * FROM dbo.CustPassMaster WHERE CustNum = '12345678'

它完全忽略索引。这让我感到困惑,因为我还有另一个表,CustDataMaster其中包含更多列(55),其中一个是CustNum varchar(8)。我IX_dbo_CustDataMaster_CustNum在此表的此列()上创建了一个索引,并使用了几乎相同的查询:

SELECT * FROM dbo.CustDataMaster WHERE CustNum = '12345678'

它使用我创建的索引。

这背后有什么具体的理由吗?为什么要使用from的索引CustDataMaster,而不使用from的索引CustPassMaster?是由于列数少吗?

第一个查询返回66行。对于第二个,返回1行。

另外,还要注意:CustPassMaster具有4991条记录和CustDataMaster5376条记录。这可能是忽略索引的原因吗?CustPassMaster也有具有相同CustNum值的重复记录。这是另一个因素吗?

我将此主张基于两个查询的实际执行计划结果。

这是DDL CustPassMaster(具有未使用的索引的DDL ):

CREATE TABLE dbo.CustPassMaster(
    [CustNum] [varchar](8) NOT NULL,
    [Username] [char](15) NOT NULL,
    [Password] [char](15) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustPassMaster_CustNum] ON dbo.CustPassMaster
(
    [CustNum] ASC
) WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

和DDL用于CustDataMaster(我省略了许多无关的字段):

CREATE TABLE dbo.CustDataMaster(
    [CustNum] [varchar](8) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustDataMaster_CustNum] ON dbo.CustDataMaster
(
    [CustNum] ASC
)WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

我在那些表中都没有聚集索引,只有一个非聚集索引。

忽略数据类型与存储的数据类型不完全匹配的事实。这些字段是来自IBM AS / 400 DB2数据库的备份,并且是它们的兼容数据类型。(我必须能够使用完全相同的查询来查询此备份数据库,并获得完全相同的结果。)

此数据用于SELECT语句。我没有做任何INSERT/ UPDATE/ DELETE上陈述时,除备份应用程序从AS / 400复制数据。


可能值得阅读有关从非集群到集群的临界点的文章。sqlskills.com/blogs/kimberly/the-tipping-point-query-answers
Mark Sinkinson 2015年

3
这就是区别。如果第一个查询使用您的索引,则必须执行65次查询。这很贵。第二个查询只需执行一个。
亚伦·伯特兰

Answers:


18

如果SQL Server认为使用索引比直接使用基础表更方便,则通常将使用索引。

基于成本的优化器似乎认为实际使用相关索引会更加昂贵。如果SELECT *您只是简单地而不是这样做,则可能会看到它使用索引SELECT T1Col1

当您SELECT *告诉SQL Server返回表中的所有列时。若要返回这些列,SQL Server 必须WHERE从表本身(聚集索引或堆)中读取与语句条件匹配的行的页面。SQL Server可能正在考虑从表中获取其余列所需的读取数量,这意味着它还可能直接扫描表。查看实际查询和查询使用的实际执行计划将很有用。


3
因此,对我来说,更明显,更理想的解决方案是限制我选择的列,并将其包括在INCLUDE索引的子句中?
Der Kommissar 2015年

1
那很可能会产生很大的变化。将查询返回的所有列添加到INCLUDE子句中可能会使SQL Server使用索引。话虽如此,您正在尝试优化什么?在我看来,如果您的表的平均行大小为100字节,则5000行仅约500kb的数据,可能不值得花任何时间在上面。
Max Vernon

1
的平均行大小为0.30KB,对于Table1则为0.53KB Table2。所有这些数据都是从AS / 400(IBM System i)导入的,任何内容都没有PK。在人们提到应用程序有时非常慢之后,我今天手动创建了所有索引。
Der Kommissar 2015年

10

为了使用索引,因为您正在执行select *,那么SQL Server必须首先从索引中读取与where子句中的值匹配的每一行。基于此,它将获得每一行的聚簇索引值,然后必须与聚簇索引分开查找每个索引值(=键查找)。由于您说的值不是唯一的,因此SQL Server使用统计信息来估计它必须执行此键查找的次数。

扫描非聚集索引+键查找的成本估算很可能超过聚集索引扫描的成本估算,这就是为什么忽略索引的原因。

您可以尝试使用set statistics io on,然后使用索引提示来查看使用索引时I / O成本实际上是否较小。如果相差很大,则可以查看统计信息(如果这些统计信息已过时)。

另外,如果您的SQL实际上使用的是变量而不是确切的值,则这也可能是由参数嗅探引起的(=用于创建计划的先前值在表中有很多行)。


1

那可能就是原因。优化器基于成本,并根据每个执行路径具有的“成本”来决定选择哪种路径。最大的代价是将数据从磁盘传输到内存。如果优化器计算出要花费更多时间读取索引和数据,那么它可能会决定跳过索引。行越大,占用的磁盘块越多。

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.