sp_executesql 什么时候刷新查询计划?

jam*_*wis 13 sql-server stored-procedures execution-plan

您必须原谅我的天真,因为我不是 DBA,但我的理解是,随着时间的推移,必须重新编译数据库更改和存储过程的统计信息,以使查询计划与最新的统计信息保持同步。

假设我在我的数据库中的存储过程,在对重新编译的最新统计一些固定的间隔,是什么在衬里的存储过程的代码,并在包裹它的含义sp_executesql的语句?我是否会丢失过去作为过程重新编译的一部分发生的查询计划的刷新?

如果在进行此更改之前我需要考虑其他任何事项(权限除外),那么我将不胜感激。

我在 MSDN 上读到这个:

SQL Server 查询优化器将新的 Transact-SQL 字符串与现有执行计划相匹配的能力受到字符串文本中不断变化的参数值的阻碍,尤其是在复杂的 Transact-SQL 语句中。

因此,假设我尝试内联和换行的存储过程sp_executesql确实包含一些参数,这是否是说虽然我的执行计划已缓存,但我让 SQL Server 更难找到和重用它?

Aar*_*and 7

来自 MSDN 的线路正在谈论使用EXEC(),如下所示:

SET @sql = 'SELECT foo FROM dbo.bar WHERE x = ''' + @x + ''';';
EXEC(@sql);
Run Code Online (Sandbox Code Playgroud)

在我的测试中,现代版本的 SQL Server 仍然能够重用这样的计划,但可能还有其他变量(例如版本,或者例如如果您WHERE根据某些参数的存在添加条件子句 - 在这种情况下将生成不同的计划)。

如果使用,sp_executesql则参数值仍然会导致参数嗅探问题(就像使用普通 SQL 一样),但这与 SQL Server 是否可以重用计划无关。这个计划会被反复使用,就像你根本没有使用sp_executesql过一样,除非会导致直接查询被重新编译的变量,在这种情况下,这个计划也会被重新编译(本质上,SQL Server 不会使用计划存储任何内容,该计划表示“这是从 sp_executesql 执行的,但这个不是):

SET @sql = N'SELECT foo FROM dbo.bar WHERE x = @x;';
EXEC sys.sp_executesql @sql, N'@x varchar(32)', @x;
Run Code Online (Sandbox Code Playgroud)

作为奖励,它具有针对动态 SQL 的内置保护,并避免您担心由于字符串分隔符而将单引号加倍。我在这里写了一些博客,请在这里这里阅读 SQL 注入。

如果您有计划再利用和/或参数嗅探问题,有些事情你应该考虑是OPTION (RECOMPILE)OPTIMIZE FORoptimize for ad hoc workloadssimple/forced parameterization。我在回应最近的网络广播时回答了一些类似的问题,可能值得一读:

要点是:不要害怕使用sp_executesql,而是只在需要时使用它,并且只有在遇到实际性能问题时才花费精力过度优化它。上面的例子很糟糕,因为没有理由在这里使用动态 SQL - 我写这个答案是假设你有一个合法的用例。