查询优化器建议添加索引而不是使用现有索引

Hoo*_*cat 7 performance index sql-server nonclustered-index query-performance

我试图确定为什么 SQL Server 中的查询优化器建议创建一个新索引,而不是使用似乎足以进行查询的现有索引。

首先是桌子。列名已更改以保护无辜者:-)

CREATE TABLE [myTable] (
  [id] [int] IDENTITY(1,1) NOT NULL,
  [serialNumber] [varchar](12) NOT NULL,
  [sName] [varchar](64) NOT NULL,
  [meanValue] [int] NOT NULL,
  [range] [int] NOT NULL,
  [modifiedDate] [datetime] NOT NULL,
  CONSTRAINT [PK_myTable] PRIMARY KEY CLUSTERED ( [id] ASC )
)
Run Code Online (Sandbox Code Playgroud)

创建有问题的索引:

CREATE NONCLUSTERED INDEX [IDX_myIndex]
ON [myTable] ([serialNumber], [sName], [meanValue], [range])
INCLUDE ([modifiedDate])
Run Code Online (Sandbox Code Playgroud)

添加数据以使用您选择的生成器进行测试;-) 运行以下查询(表只有几百万条记录)

SELECT TOP 1000
  [serialNumber],
  [sName],
  [meanValue],
  [range],
  [modifiedDate]
FROM [myTable]
WHERE [serialNumber] = 137802
AND [sName] = 'A Name'
Run Code Online (Sandbox Code Playgroud)

查询优化器建议使用新索引,其中额外的 where 子句包含在 INCLUDE 中,而不是键的一部分:

CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [dbo].[myTable] ([sName])
INCLUDE ([serialNumber],[meanValue],[range],[modifiedDate])
Run Code Online (Sandbox Code Playgroud)

我的印象是,只要 WHERE 子句的顺序代表索引列的顺序,就会使用包含更多列的更广泛的索引作为索引。

如果我也在修改数据的 WHERE 上使用索引并且查询优化器不会抱怨:

SELECT TOP 1000
  [serialNumber],
  [sName],
  [meanValue],
  [range],
  [modifiedDate]
FROM [myTable]
WHERE [serialNumber] = 137802
AND [sName] = 'A Name'
AND ([modifiedDate] >= '2000-04-25' AND [modifiedDate] < '2019-04-30') 
Run Code Online (Sandbox Code Playgroud)

DBA 链接
SQL Server 2008R2 - Why is my index not used表明索引键和包含与 SELECT 语句之间更密切的相关性有助于确定索引的使用(但在我的示例中它们基本相同)。我有很多行,这可能满足行使用概率测试,并且没有 NULL - 因此否定了索引的 NULL 效果。

我认为,也许是错误的,索引A, B, C, D将覆盖查询 where A, B, C, or A, B, orA将运行。这个假设是错误的吗?我意识到可能存在使这个基本概念偏离的边缘条件,但在基本层面上,这不是它应该如何工作的粗略吗?

在此先感谢您的帮助,指出我的愚蠢之处,认识到我需要去(回)DB 学校等...... :-)

ype*_*eᵀᴹ 14

您的索引对于查询来说似乎很好(即覆盖),应该使用它。真正的问题是查询本身,特别是这个隐藏隐式转换的条件:

WHERE [serialNumber] = 137802
Run Code Online (Sandbox Code Playgroud)

根据 SQL Server 的数据类型优先级,当比较两个不同数据类型的值时,将优先级较低的数据类型的值转换为优先级较高的数据类型。不幸的是,int在列表中高于varchar. 由于列 ( serialNumber) 值被转换为整数,因此使用索引的任何希望都破灭了。该列是索引的第一个位置,导致优化器不使用该索引并搜索替代项(以及建议)。

解决方案是不要对WHERE条件中的列进行任何隐式或显式转换。只需使用:

WHERE [serialNumber] = '137802'
Run Code Online (Sandbox Code Playgroud)