我最近读完 《SQL Server 2008 中的计划缓存》 ,我感到很困惑。看起来,除了完全刷新计划缓存或明确要求重新编译存储过程之外,从 SQL Server 2008 开始,存储过程的重新编译都是在语句级别而不是存储过程级别完成的。
那么,除了显式刷新缓存或要求重新编译(例如WITH RECOMPILE
)之外,什么可以在 SQL Server 2019 中重新编译完整的存储过程,而不仅仅是重新编译单个语句呢?
举一个我感到困惑的例子,请考虑以下过程。
CREATE PROCEDURE FOO AS
BEGIN
SELECT * INTO #temp1 FROM table1
INSERT BAR1 SELECT * FROM #temp1
INSERT BAR2 SELECT * FROM #temp1
END
我可以想到很多可能导致SELECT * INTO #temp1 FROM table1
重新编译的事情,但是如果没有下一行也重新编译,那么重新编译会很奇怪。这让我觉得 SQL Server 中一定有一些东西会导致整个存储过程重新编译。
不多
除了您的问题中列出的内容之外,存储过程确实没有常见的通用原因总是为每个语句重新编译新计划,至少对于您的示例中的语句来说是这样。
SQL Server 提供了几种不同的方法来跟踪重新编译,但它们都是语句级的。也许最容易获得答案的是扩展事件。我遇到过在对象级别没有跟踪重新编译的事件。
截至 SQL Server 2022,这列出了 23 个重新编译的原因,其中包括“不是重新编译”。
然而
我知道有几件事会阻止计划缓存,这会使模块看起来每次都需要重新编译(尽管实际上我觉得这更像是每次都进行编译)。
正如保罗在他的帖子中指出的那样:
请点击链接查看几个示例。
我还没有发布关于此问题的帖子,但总体思路是,当
IF
存储过程中存在未探索的分支引用不存在的对象时,整个批次是不可缓存的。请随意跟随下面的演示。请注意,如果您尝试获取如下所示的存储过程的估计计划,或者如果您以探索具有不存在对象的分支的方式运行它,它将引发错误。
当然,您可以显式定义存储过程以在每次运行时重新编译,使用重新编译选项,如下所示:
替代选项还可以使用重新编译提示执行存储过程:
或者显式调用系统过程
sp_recompile
: