当我将值设置为变量时,为什么我的SQL语句需要花费N倍的时间才能运行?

时间:2010-12-22 21:57:40

标签: sql variables indexing long-integer

我首先要说的是,这并不是我想要实现的目标。我已经愚弄了这个问题很多,以便更清楚地解决我的问题。

我在表(CallDetail)上有两个值的非聚集索引,TermDate(int)和SourceSystemID(int)。为了完整起见,我将在此处包含指数的精确定义:

CREATE NONCLUSTERED INDEX [CallDetail_TermDateSourceSystemID] ON [dbo].[CallDetail] 
(
    [TermDate] ASC,
    [SourceSystemID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

我遇到的问题是,当我对这个表运行两个几乎相同的查询时,我得不到相同的结果(不要与结果集混淆)。第一个查询在不到一秒的时间内运行并返回大约10,000行。第二个查询在执行时继续运行,直到我在大约30分钟后取消它。

查询1(~1秒):

SELECT
    *
FROM
    CallDetail
WHERE
    CallDetail.TermDate >= 1101221 AND
    SourceSystemID = 1

查询2(> 30分钟):

DECLARE @TermDate AS INT
SET @TermDate = 1101221

SELECT
    *
FROM
    CallDetail
WHERE
    CallDetail.TermDate >= @TermDate AND
    SourceSystemID = 1

我想要注意的是查询执行计划告诉我在索引中“包含”该表的所有列。我发现这是完全错误的。我还要注意,如果我只选择TermDate和SourceSystemID而不是*,我会在大约1秒内得到结果。

是否有理由在使用变量而不是将值硬编码到需要更长时间的位置?我完全被这个难以接受,任何帮助都将非常感激。

谢谢!

Christopher Haws

3 个答案:

答案 0 :(得分:5)

好的,我用我的查询重现了这个情况:

declare @a as int
set @a = 12972100
select * from MyTable where (MyColumn > @a)

在阅读了marc_s的帖子之后,我做到了这一点:

declare @a as int
set @a = 12972100
select * from MyTable where (MyColumn > @a) option (recompile)

一切都很快了!

答案 1 :(得分:2)

您最有可能患parameter sniffinghere

您可以比较两个查询的查询计划吗?具有固定值的那个与具有参数的那个?他们会给你相同的实际执行计划吗?

请记住做一次正确的“清理”并在两次运行之间发出DBCC FREEPROCCACHE,这样两者就不会相互影响......

答案 2 :(得分:1)

更新

鉴于这些评论,我很难过。你说你已经愚弄了细节来解释这个问题,可能是其中一些细节导致了这个问题。您是否在上面显示的确切示例中看到了相同的问题?

我看到包含演员或转换的更复杂的查询有类似的问题 - 这是你愚蠢的事情之一吗?

或许这可能是Marc建议的参数嗅探。


原始答案

这是因为它必须将列表中的每个值转换为int类型(TermDate列必须是其他内容。)

在第一个查询中,优化器是智能的,并且知道它可以将常量强制转换为列的类型,并且这样做。

此外,当必须为每一行进行强制转换时,它不能使用索引。

更改此行

 DECLARE @TermDate AS INT

与列相同的类型,您会很高兴。