为什么 SQL Server 会忽略索引?

Der*_*sar 16 sql-server index-tuning sql-server-2012

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

SELECT * FROM dbo.CustPassMaster WHERE CustNum = '12345678'
Run Code Online (Sandbox Code Playgroud)

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

SELECT * FROM dbo.CustDataMaster WHERE CustNum = '12345678'
Run Code Online (Sandbox Code Playgroud)

它使用我创建的索引。

这背后有什么具体的原因吗?为什么它会使用 from 的索引CustDataMaster,而不是from 的索引CustPassMaster?是因为列数少吗?

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

另外,补充说明:CustPassMaster有 4991 条记录,CustDataMaster有 5376 条记录。这可能是忽略索引的原因吗?CustPassMaster也有具有相同CustNum值的重复记录。这是另一个因素吗?

我基于这两个查询的实际执行计划结果提出了这一主张。

这是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]
Run Code Online (Sandbox Code Playgroud)

和 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]
Run Code Online (Sandbox Code Playgroud)

我在其中任何一个表上都没有聚集索引,只有一个非聚集索引。

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

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

Han*_*non 18

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

基于成本的优化器似乎认为实际使用相关索引的成本更高。您可能会看到它使用索引,如果您不这样做SELECT *,您只需SELECT T1Col1.

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

  • 因此,一个更明显和最佳的解决方案是限制我选择的列,并将它们包含在索引的“INCLUDE”子句中? (3认同)

Jam*_*s Z 10

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

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

您可以尝试使用set statistics io on然后使用索引提示来查看使用索引时 I/O 成本是否实际上更小。如果差异很大,您可以查看统计数据,如果这些数据已经过时。

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