为什么这个查询不使用正确的索引?

Ang*_*ker 3 sql sql-server indexing sql-server-2005

表定义:

CREATE TABLE [dbo].[AllErrors](
  [ID] [int] IDENTITY(1,1) NOT NULL,
  [DomainLogin] [nvarchar](50) NULL,
  [ExceptionDate] [datetime] NULL,
  [ExceptionDescr] [nvarchar](max) NULL,
  [MarketName] [nvarchar](50) NULL,
  [Version] [nvarchar](50) NULL,
  CONSTRAINT [PK_AllErrors] PRIMARY KEY CLUSTERED ([ID] ASC)
)

-- Add an index on the date
CREATE NONCLUSTERED INDEX [IX_ExceptionDate] ON [dbo].[AllErrors] ([ExceptionDate] ASC)
Run Code Online (Sandbox Code Playgroud)

我运行这个查询:

declare @yesterday datetime
select @yesterday = getdate() - 1

SELECT * INTO #yst
from AllErrors 
where ExceptionDate between @yesterday and @yesterday + 1
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

此代码不使用my IX_ExceptionDate(从执行计划中收集).它对主键索引执行群集扫描.但是,下面的代码确实使用IX_ExceptionDate索引:

SELECT * INTO #yst
from AllErrors 
where ExceptionDate between @yesterday and @yesterday + 1
  AND ExceptionDate = ExceptionDate
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

为什么是这样?

编辑:添加了视觉执行计划.

编辑:下面的文本执行计划.

查询1:

| - 表格插入(OBJECT:([#yst]),SET :( [#yst].[ID] = [Expr1006],[#yst].[DomainLogin] = [MarketStats].[dbo].[AllErrors ].[DomainLogin],[#yst].[ExceptionDate] = [MarketStats].[dbo].[AllErrors].[ExceptionDate],[#yst].[ExceptionDescr] = [MarketStats].[dbo].[AllErrors ].[ExceptionDescr],[#yst].[MarketName] = [MarketStats].[dbo].[AllErrors].[MarketName],[#yst].[Version] = [MarketStats].[dbo].[AllErrors ].[Version]))| - Top(ROWCOUNT est 0)| - 计算标量(DEFINE :( [Expr1006] = setidentity([MarketStats].[dbo].[AllErrors].[ID],( - 7 ),(0),N'#yst')))| - 分簇索引扫描(OBJECT:([MarketStats].[dbo].[AllErrors].[PK_AllErrors]),WHERE:([MarketStats].[dbo] ].[AllErrors].[ExceptionDate]> = [@昨天] AND [MarketStats].[dbo].[AllErrors].[ExceptionDate] <= [@ yesterday] +'1900-01-02 00:00:00.000' ))

查询2:

| - 表格插入(OBJECT:([#yst]),SET :( [#yst].[ID] = [Expr1006],[#yst].[DomainLogin] = [MarketStats].[dbo].[AllErrors ].[DomainLogin],[#yst].[ExceptionDate] = [MarketStats].[dbo].[AllErrors].[ExceptionDate],[#yst].[ExceptionDescr] = [MarketStats].[dbo].[AllErrors ].[ExceptionDescr],[#yst].[MarketName] = [MarketStats].[dbo].[AllErrors].[MarketName],[#yst].[Version] = [MarketStats].[dbo].[AllErrors ].[Version]))| - Top(ROWCOUNT est 0)| - 计算标量(DEFINE :( [Expr1006] = setidentity([MarketStats].[dbo].[AllErrors].[ID],( - 7 ),(0),N'#yst')))| - 嵌套循环(内部连接,外部参考:([MarketStats].[dbo].[AllErrors].[ID],[Expr1008])优化为UNORDERED PREFETCH)| - 索引查询(OBJECT:([MarketStats].[dbo].[AllErrors].[IX_ExceptionDate]),SEEK:([MarketStats].[dbo].[AllErrors].[ExceptionDate]> = [@昨天]和[MarketStats].[dbo].[AllErrors].[ExceptionDate] <= [@yesterday] +'1900-01-02 00:00:00.000'),其中:([MarketStats].[dbo]. [AllErrors].[ExceptionDate] = [MarketStats].[DBO].[AllErrors].[E xceptionDate])ORDERED FORWARD)| - 集群索引查询(OBJECT:([MarketStats].[dbo].[AllErrors].[PK_AllErrors]),SEEK:([MarketStats].[dbo].[AllErrors].[ID ] = [MarketStats].[dbo].[AllErrors].[ID])LOOKUP ORDERED FORWARD)

Mar*_*ith 6

在编译查询时,它不知道变量的值是什么.你可以试试OPTION (RECOMPILE).

我认为AND在查询中添加子句(尽管从逻辑上讲它使其不再具有选择性)必须误导优化器以更高的选择性来估计查询,从而为您提供所需的计划!

你在评论中说,没有的版本ExceptionDate = ExceptionDate估计88234.8在行和版本8823.48

通常,在没有可用统计信息的情况下,SQL Server会根据谓词中比较运算符的类型回退到启发式算法.

它假定>谓词将返回30%的行,例如=谓词将返回10%的行,因此看起来它只是将它直接应用于第一次估计的结果.有趣的是,它没有考虑到这里的等于对立柱本身的事实!

cf 管理统计信息的最佳实践 - 避免在查询中使用局部变量


小智 5

简短回答:由于"SELECT*",您的查询会遇到聚簇索引:Key Lookup操作比聚簇索引扫描要昂贵得多.

查看由此产生的不同查询计划

declare @yesterday datetime
select @yesterday = getdate() - 1

SELECT * INTO dbo.#yst
from AllErrors WITH (INDEX = IX_ExceptionDate)
where ExceptionDate between @yesterday and @yesterday + 1
Run Code Online (Sandbox Code Playgroud)

declare @yesterday datetime
select @yesterday = getdate() - 1

SELECT * INTO dbo.#yst
from AllErrors
where ExceptionDate between @yesterday and @yesterday + 1
Run Code Online (Sandbox Code Playgroud)

declare @yesterday datetime
select @yesterday = getdate() - 1

SELECT ExceptionDate INTO dbo.#yst
from AllErrors
where ExceptionDate between @yesterday and @yesterday + 1
Run Code Online (Sandbox Code Playgroud)