为什么UDF使用声明的变量作为参数调用比使用硬编码参数调用更快

时间:2013-02-12 08:10:16

标签: sql-server performance tsql user-defined-functions

在我看来,我有以下非常奇怪的问题。当我按以下方式调用 UDF 时:

DECLARE @contact_id uniqueidentifier
DECLARE @group_id uniqueidentifier
SET @group_id = 'EE57E2AD-204B-4078-AFA4-11FA8375C2FD'
set @contact_id = 'E6EFCC9F-9D1C-4C38-A950-C45372F2A6D2'

SELECT COUNT( ID )  AS [CountAll] 
FROM [Document] As [Document] 
WHERE ([Document].[ID] IN (SELECT ID FROM [fs_Document_View_ee57e2ad_204b_4078_afa4_11fa8375c2fd](@contact_id, @group_id)))

它运行4秒,我得到以下执行计划:fast

当我使用硬编码参数调用 UDF 时:

SELECT COUNT( ID )  AS [CountAll] 
FROM [Document] As [Document] 
WHERE ([Document].[ID] IN (SELECT ID FROM [fs_Document_View_ee57e2ad_204b_4078_afa4_11fa8375c2fd]('E6EFCC9F-9D1C-4C38-A950-C45372F2A6D2', 'EE57E2AD-204B-4078-AFA4-11FA8375C2FD')))

我得到了这个执行计划:slow,需要91秒。

任何人都可以解释为什么会这样吗?

该函数调用4个其他嵌套函数,为它们传递相同的参数。它与项目查看权限相关。

感谢您的帮助。

更新

我使用了Ivan G.提到的this article中的选项2。

问题是参数嗅探,选项2解决了问题。

  

解决参数嗅探问题的另一种方法是完全禁用参数嗅探。这不是通过开关或数据库选项完成的,但可以从存储过程代码的脚本中的>完成。以下是我创建>存储过程的示例,以便禁用参数嗅探:

DROP PROC [dbo].[DisplayBillingInfo]
GO
CREATE PROC [dbo].[DisplayBillingInfo]
  @BeginDate DATETIME,
  @EndDate DATETIME
WITH RECOMPILE
AS
DECLARE @StartDate DATETIME;
DECLARE @StopDate DATETIME;
SET @StartDate = @BeginDate;
SET @StopDate = @EndDate;
SELECT BillingDate, BillingAmt
  FROM BillingInfo
  WHERE BillingDate between @StartDate AND @StopDate; 
  

要禁用参数嗅探,我所做的就是改变方式   参数值在存储过程中使用。通过创造   我内部有两个不同的局部变量(@StartDate和@EndDate)   过程,将这些变量设置为传递的参数,然后   使用BETWEEN条件中的局部变量,我能够   禁用参数嗅探。参数嗅探被禁用,因为   优化器无法识别参数中的值   实际的SELECT语句。因为SQL Server无法分辨出什么   用于调用存储过程的参数值,   优化程序根据统计信息创建通用计划。

     

当我使用上面的代码执行我的存储过程时,使用其中任何一个   编辑的日期范围很窄或者数年的日期   执行计划始终执行“索引扫描”操作。我可以告诉你   参数嗅探被关闭因为我知道短程   日期通常会创建索引搜索操作。

1 个答案:

答案 0 :(得分:2)

我认为这是由于参数化。您的查询的第一个版本是参数化的,而第二个版本则不是。 “参数化的查询需要较少的重新编译,动态构建的查询需要经常进行编译和重新编译”(source

对于使用参数构建的查询版本,创建执行计划然后重复使用:“如果SQL查询具有参数,SQL Server会创建一个针对它们的执行计划,以通过称为'参数嗅探的过程来提高性能'。这个计划是存储和重用的,因为它通常是最好的执行计划“(source)。