简体   繁体   English

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

[英]Why doesn't this query use the proper index?

Table definition: 表定义:

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)

I run this query: 我运行这个查询:

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

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

在此输入图像描述

This code does not use my IX_ExceptionDate (as gleaned from the execution plan). 此代码不使用我的IX_ExceptionDate (从执行计划中收集)。 It does a clustered scan on the primary key index. 它对主键索引执行群集扫描。 However, the code below does use the IX_ExceptionDate index: 但是,下面的代码确实使用了IX_ExceptionDate索引:

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

在此输入图像描述

Why is this? 为什么是这样?

EDIT: Visual Execution Plan added. 编辑:添加了视觉执行计划。

EDIT: Textual Execution Plans below. 编辑:下面的文本执行计划。

Query 1: 查询1:

|--Table Insert(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) |--Compute Scalar(DEFINE:([Expr1006]=setidentity([MarketStats].[dbo].[AllErrors].[ID],(-7),(0),N'#yst'))) |--Clustered Index Scan(OBJECT:([MarketStats].[dbo].[AllErrors].[PK_AllErrors]), WHERE:([MarketStats].[dbo].[AllErrors].[ExceptionDate]>=[@yesterday] AND [MarketStats].[dbo].[AllErrors].[ExceptionDate]<=[@yesterday]+'1900-01-02 00:00:00.000')) | - 表格插入(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' ))

Query 2: 查询2:

|--Table Insert(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) |--Compute Scalar(DEFINE:([Expr1006]=setidentity([MarketStats].[dbo].[AllErrors].[ID],(-7),(0),N'#yst'))) |--Nested Loops(Inner Join, OUTER REFERENCES:([MarketStats].[dbo].[AllErrors].[ID], [Expr1008]) OPTIMIZED WITH UNORDERED PREFETCH) |--Index Seek(OBJECT:([MarketStats].[dbo].[AllErrors].[IX_ExceptionDate]), SEEK:([MarketStats].[dbo].[AllErrors].[ExceptionDate] >= [@yesterday] AND [MarketStats].[dbo].[AllErrors].[ExceptionDate] <= [@yesterday]+'1900-01-02 00:00:00.000'), WHERE:([MarketStats].[dbo].[AllErrors].[ExceptionDate]=[MarketStats].[dbo].[AllErrors].[E | - 表格插入(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) |--Clustered Index Seek(OBJECT:([MarketStats].[dbo].[AllErrors].[PK_AllErrors]), SEEK:([MarketStats].[dbo].[AllErrors].[ID]=[MarketStats].[dbo].[AllErrors].[ID]) LOOKUP ORDERED FORWARD) xceptionDate])ORDERED FORWARD)| - 集群索引查询(OBJECT:([MarketStats]。[dbo]。[AllErrors]。[PK_AllErrors]),SEEK:([MarketStats]。[dbo]。[AllErrors]。[ID ] = [MarketStats]。[dbo]。[AllErrors]。[ID])LOOKUP ORDERED FORWARD)

It doesn't know what the value of the variables will be when it compiles the query. 在编译查询时,它不知道变量的值是什么。 You could try OPTION (RECOMPILE) . 你可以试试OPTION (RECOMPILE)

I presume that the addition of the AND clause in the query (even though logically it makes it no more selective at all) must mislead the optimiser into estimating the query with greater selectivity thus giving you the plan that you wanted! 我认为在查询中添加AND子句(即使在逻辑上它使它不再具有选择性)必须误导优化器以更高的选择性估计查询,从而为您提供所需的计划!

You say in the comments that the version without the ExceptionDate = ExceptionDate is estimated at 88234.8 rows and the version with 8823.48 您在评论中说没有ExceptionDate = ExceptionDate的版本估计为88234.8行,版本为8823.48

Generally in the absence of usable statistics SQL Server falls back to heuristics dependant upon the type of comparison operator in the predicate. 通常,在没有可用统计信息的情况下,SQL Server会根据谓词中比较运算符的类型回退到启发式算法。

It assumes that a > predicate will return 30% of the rows for example and that an = predicate will return 10% of the rows so it looks like it is just applying that directly to the result of the first estimate. 它假定>谓词将返回30%的行,例如,a =谓词将返回10%的行,因此看起来它只是直接将其应用于第一次估计的结果。 Interesting that it does not take account of the fact that the equals is against the column itself here! 有趣的是,它没有考虑到这里的等于对立柱本身的事实!

cf Best Practices for Managing Statistics - Avoid use of local variables in queries cf 管理统计信息的最佳实践 - 避免在查询中使用局部变量

Short answer: Because of the "SELECT *", your query hits the clustered index: the Key Lookup operation is much more costly than a clustered index scan. 简短回答:由于“SELECT *”,您的查询会遇到聚簇索引:Key Lookup操作比聚簇索引扫描要昂贵得多。

See the differing query plans resulting from 查看由此产生的不同查询计划

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

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

AND

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

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

AND

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM