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

时间:2011-05-12 17:23:30

标签: sql sql-server sql-server-2005 indexing

表格定义:

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)

我运行此查询:

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

SELECT * INTO #yst
from AllErrors 
where ExceptionDate between @yesterday and @yesterday + 1

enter image description here

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

SELECT * INTO #yst
from AllErrors 
where ExceptionDate between @yesterday and @yesterday + 1
  AND ExceptionDate = ExceptionDate

enter image description here

为什么会这样?

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

编辑:下面的文字执行计划。

查询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] [版]))          | - 顶部(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]> = [@昨天]和[MarketStats]。[dbo]。[AllErrors]。[ExceptionDate]< = [@昨天] +'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] [版]))          | - 顶部(ROWCOUNT est 0)               | - 计算标量(DEFINE :( [Expr1006] = setidentity([MarketStats]。[dbo]。[AllErrors]。[ID],( - 7),(0),N'#yst')))                    | - 嵌套循环(内部连接,外部参考:([MarketStats]。[dbo]。[AllErrors]。[ID],[Expr1008])优化使用未定义的预置)                         | --Index Seek(OBJECT:([MarketStats]。[dbo]。[AllErrors]。[IX_ExceptionDate]),SEEK:([MarketStats]。[dbo]。[AllErrors]。[ExceptionDate]> = [@yesterday ]和[MarketStats]。[dbo]。[AllErrors]。[ExceptionDate]< = [@yesterday] +'1900-01-02 00:00:00.000'),其中:([MarketStats]。[dbo]。 [AllErrors]。[ExceptionDate] = [MarketStats]。[dbo]。[AllErrors]。[ExceptionDate])ORDERED FORWARD)                         | --Clustered Index Seek(OBJECT:([MarketStats]。[dbo]。[AllErrors]。[PK_AllErrors]),SEEK:([MarketStats]。[dbo]。[AllErrors]。[ID] = [MarketStats]。 [dbo]。[AllErrors]。[ID])LOOKUP ORDERED FORWARD)

2 个答案:

答案 0 :(得分:6)

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

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

您在评论中说,没有ExceptionDate = ExceptionDate的版本估计为88234.8行,而版本为8823.48

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

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

c.f。 Best Practices for Managing Statistics - Avoid use of local variables in queries

答案 1 :(得分: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

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

SELECT * INTO dbo.#yst
from AllErrors
where ExceptionDate between @yesterday and @yesterday + 1

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

SELECT ExceptionDate INTO dbo.#yst
from AllErrors
where ExceptionDate between @yesterday and @yesterday + 1