SQL Server:内联和存储过程执行之间的意外性能问题

问题描述

我在研究特定存储过程的性能问题时遇到了各种各样的谜。我没有创建那个存储过程,而且在 join 语句中嵌套选择的逻辑相当丑陋,等等......

但是,当我将存储过程的逻辑直接复制到新的查询窗口中时,在顶部添加参数并执行它时,这运行时间不到 400 毫秒。然而,当我调用存储过程并使用完全相同的参数值执行它时,运行需要 23 秒!

这对我来说完全没有意义!

是否有一些服务器级别的设置需要我检查,哪些可能会对此产生影响?

谢谢

解决方法

重新编译您的存储过程。

Microsoft documentation

首次编译或重新编译过程时,过程的查询计划会针对数据库及其对象的当前状态进行优化。如果数据库的数据或结构发生重大更改,则重新编译过程会针对这些更改更新并优化过程的查询计划。这可以提高程序的处理性能。

强制过程重新编译的另一个原因是抵消过程编译的“参数嗅探”行为。当 SQL Server 执行过程时,该过程在编译时使用的任何参数值都包含在生成查询计划的过程中。如果这些值代表随后调用过程所使用的典型值,则过程每次编译和执行时都会从查询计划中受益。如果程序上的参数值经常不典型,则强制重新编译程序并根据不同的参数值制定新计划可以提高性能。

如果您自己运行 SP 的查询(可能在 SSMS 中),它们会被编译并运行。

如何重新编译您的 SP? (请参阅上面链接的文档页面。)

  • 您可以重新运行其定义以重新编译一次。如果该过程是在数据库生命周期中很久以前首次定义的并且您的表增长了很多,那么这可能就足够了。

  • 您可以将 CREATE PROCEDURE ... WITH RECOMPILE AS 放入其定义中,以便每次运行时都会重新编译。

  • 运行时可以EXEC procedure WITH RECOMPILE;

  • 您可以重新启动 SQL Server。 (这可能会很麻烦;神奇地启动服务器可以让糟糕的事情停止发生,而且没有人完全确定原因。)

重新编译需要一些时间。但与执行计划不佳的复杂查询相比,它花费的时间要少得多。

,

所以...我最终将连接上的嵌套选择转换为表变量,现在 sproc 的执行时间为 60 毫秒,而内嵌 sql 的执行时间为 250=ms。

但是,我仍然不明白为什么 sproc 的执行速度比使用原始嵌套 sql 逻辑的内嵌 sql 版本慢这么多?

我的意思是,两者都使用完全相同的 sql 逻辑,那么为什么 sproc 需要 23 秒而内嵌是 400 毫秒?