Sql 有没有办法延迟编译存储过程的执行计划?

Sql 有没有办法延迟编译存储过程的执行计划?,sql,sql-server,sql-server-2005,tsql,parameter-sniffing,Sql,Sql Server,Sql Server 2005,Tsql,Parameter Sniffing,乍一看,这看起来像是or的复制品,但我的实际问题有点不同 好吧,这件事已经困扰了我好几个小时了。我这里的例子是荒谬地抽象出来的,所以我怀疑是否有可能在本地重新创建,但它也为我的问题提供了上下文,我正在运行SQLServer2005 我有一个基本上分为两步的存储过程,构造一个临时表,用很少的行填充它,然后查询一个非常大的表来连接该临时表。它有多个参数,但最相关的是datetime@MinDate。基本上: create table #smallTable (ID int) insert into

乍一看,这看起来像是or的复制品,但我的实际问题有点不同

好吧,这件事已经困扰了我好几个小时了。我这里的例子是荒谬地抽象出来的,所以我怀疑是否有可能在本地重新创建,但它也为我的问题提供了上下文,我正在运行SQLServer2005

我有一个基本上分为两步的存储过程,构造一个临时表,用很少的行填充它,然后查询一个非常大的表来连接该临时表。它有多个参数,但最相关的是datetime@MinDate。基本上:

create table #smallTable (ID int)

insert into #smallTable
select (a very small number of rows from some other table)

select * from aGiantTable
inner join #smallTable on #smallTable.ID = aGiantTable.ID
inner join anotherTable on anotherTable.GiantID = aGiantTable.ID
where aGiantTable.SomeDateField > @MinDate
如果我只是将其作为一个普通查询来执行,通过将@MinDate声明为一个局部变量并运行它,它将生成一个最佳执行计划,该计划首先在smallTable上快速执行连接,然后在执行其他操作时只考虑aGiantTable中非常小的行子集。它似乎意识到smallTable很小,所以从它开始会很有效。这很好

但是,如果我使用@MinDate作为参数来创建一个存储过程,它将生成一个完全低效的执行计划。我每次都在重新编译它,所以它不是一个坏的缓存计划…至少,我确实希望它不是

但这就是奇怪的地方。如果我将过程更改为以下内容:

declare @LocalMinDate datetime
set @LocalMinDate = @MinDate --where @MinDate is still a parameter

create table #smallTable (ID int)

insert into #smallTable
select (a very small number of rows from some other table)

select * from aGiantTable
inner join #smallTable on #smallTable.ID = aGiantTable.ID
inner join anotherTable on anotherTable.GiantID = aGiantTable.ID
where aGiantTable.SomeDateField > @LocalMinDate
然后它给了我一个有效的计划

因此,我的理论是:当作为普通查询而不是存储过程执行时,它会等到最后一分钟才为昂贵的查询构造执行计划,因此查询优化器知道smallTable很小,并使用该信息给出有效的计划

但是当作为存储过程执行时,它会一次创建整个执行计划,因此不能使用这一信息来优化计划

但是为什么使用本地声明的变量会改变这一点呢?为什么这会推迟执行计划的制定?这是真的吗?如果是这样的话,有没有一种方法可以强制延迟编译,即使不以这种方式使用局部变量也是如此

更一般地说,是否有人知道何时为存储过程的每个步骤创建执行计划?谷歌没有提供任何有用的信息,但我认为我找的东西不对。还是我的理论完全没有根据

编辑:自从发布以来,我已经了解了参数嗅探,我认为这是导致执行计划过早编译的原因,除非存储过程确实一次编译完,所以我的问题仍然是——您能强制延迟吗?或者完全禁用嗅探功能

这个问题是学术性的,因为我可以通过将select*from aGiantTable替换为

或者只是吸收它并掩盖参数,但这种不一致性仍然让我很好奇

tl;dnr 这是一个非常长的问题,所以简言之:


完整执行计划是在第一次调用存储过程时创建的,还是在存储过程执行时创建的?也就是说,如果存储过程由多个步骤组成,那么每个步骤的执行计划是在第一次调用该过程时创建的,还是仅在以前的步骤再次执行完毕后才创建的,还是在第一次调用该过程时创建的?

请参阅一些其他文章:


请注意,您还可以使用“重新编译查询”选项来解决参数嗅探问题。这是参数嗅探,如果您没有SQL Server 2008并针对未知进行优化,则最好使用您发现的局部变量屏蔽参数。

我已经阅读了您链接的源代码。我认为你不明白重新编译的原因——这是问题的根源。正如我所说,我已经在每次执行时重新编译查询了。如果我缓存了一个有效的计划,它将提供一个解决方案,但这并不能解决根本的问题。您每次测试都使用相同的参数值吗?顺便说一句:存储过程最初是在第一次输入时编译的,并针对传递到任何输入参数中的值进行优化。最后,我使用了一个优化提示,并找到了一个值来生成最佳计划,这给了我几乎相同的效果。
select * from (select * from aGiantTable where ID in (select ID from #smallTable)) as aGiantTable