2011-05-12 75 views
3

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

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:

| --Table插入(OBJECT:([#YST]),SET:([#YST] [ID] = [Expr1006],[#YST] [ DomainLogin] = [MarketStats]。[dbo]。[AllErrors]。[DomainLogin],[#yst]。[ExceptionDate] = [MarketStats]。[dbo]。[AllErrors]。[ExceptionDate],[#yst]。[例外情况] = [MarketStats]。[dbo]。[AllErrors]。[ExceptionDescr],[#yst]。[MarketName] = [MarketStats]。[dbo]。[AllErrors]。[MarketName],[#yst]版本] = [MarketStats]。[dbo]。[AllErrors]。[Version])) | --Top(ROWCOUNT est 0) | --Compute Scalar(DEFINE:([Expr1006] = setidentity([MarketStats]。 ([市场统计])。 [所有错误]。[PK_AllErrors]),地点:([MarketStats]。[dbo]。[AllErrors]。[ExceptionDate]> = [@昨天]和[MarketStats]。[dbo]。[AllErrors]。 [ExceptionDate] < = [@昨天] + '1900年1月2日00:00:00.000'))

问题2:

| --Table插入(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]。 [市场名称] = [MarketStats]。[dbo]。[AllErrors]。[MarketName],[#yst]。[Version] = [MarketStats]。[dbo]。[AllErrors]。[Version ])) | --Top(ROWCOUNT est 0) | --Compute Scalar(DEFINE:([Expr1006] = setidentity([MarketStats]。[dbo]。[AllErrors]。[ID],( - 7), (外部参考:([MarketStats]。[dbo]。[AllErrors]。[ID],[Expr1008])使用无预置)([市场策略]。[市场策略]。[市场策略]。[所有错误]。[IX_ExceptionDate]),SEEK :(市场策略]。[dbo]。[AllErrors]。[ExceptionDate]> = [@昨天]和[MarketStats]。[dbo]。[AllErrors]。[ExceptionDate] < = [@yesterday] +'1900-01-02 00:00:00.000'),地点:([MarketStats]。[dbo]。 [AllErrors]。[ExceptionDate] = [MarketStats]。[dbo]。[AllErrors]。[ExceptionDate])ORDERED FORWARD) | - 群集索引寻找(OBJECT :(市场策略]。[dbo]。[AllErrors]。 [PK_AllErrors]),SEEK:([MarketStats]。[dbo]。[AllErrors]。[ID] = [MarketStats]。[dbo]。[AllErrors]。[查看]查看订单前进)

+0

你可以发布执行计划吗? – Lamak 2011-05-12 17:30:53

+0

你可以发布这个计划,我对'| - 集群索引扫描(OBJECT:([aspnetdb]。[dbo]。[AllErrors]。[PK_AllErrors]),WHERE:([aspnetdb]。[ dbo]。[AllErrors]。[ExceptionDate]> = [@昨天] AND [aspnetdb]。[dbo]。[AllErrors]。[ExceptionDate] <= [@ yesterday] +'1900-01-02 00:00:00.000 '))' – SQLMenace 2011-05-12 17:31:59

+0

'| - 集群索引扫描(OBJECT:([aspnetdb]。[dbo]。[AllErrors]。[PK_AllErrors]),WHERE:([aspnetdb]。[dbo]。[AllErrors]。[ExceptionDate ]> = [@昨天] AND [aspnetdb]。[dbo]。[AllErrors]。[ExceptionDate] <= [@ yesterday] +'1900-01-02 00:00:00.000'AND [aspnetdb]。[dbo] 。[AllErrors]。[ExceptionDate] = [aspnetdb]。[dbo]。[AllErrors]。[ExceptionDate]))' – SQLMenace 2011-05-12 17:32:17

回答

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

5

简短的回答:由于“SELECT *”,您的查询命中聚集索引:密钥查找操作比聚集索引扫描成本更高。

看到不同的查询计划从

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 
+0

如果您需要表中的所有数据。一旦你得到了相当数量的行,无论如何,所有的成本应该在插入。 – SRoderick 2011-05-12 18:24:59

+0

这是一个很好的想法,但结果是相同的(我确实需要表中的所有列,而不仅仅是ExceptionDate)。使用*是不好的做法,恕我直言, – AngryHacker 2011-05-12 18:30:12

+1

。更好地列举明确的字段 – 2011-05-13 08:40:12

相关问题