sqlserver-为什么用声明变量作为参数的UDF调用要比用硬编码参数的调用快得多



在我看来,我遇到了以下非常奇怪的问题。当我以以下方式调用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秒,我得到了以下执行计划:快速

当我用硬编码参数调用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')))

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

有人能解释一下为什么会发生这种事吗?

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

感谢您提前提供的帮助。

更新

我使用了Ivan G.提到的这篇文章中的选项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无法判断用于调用存储过程的参数值优化器根据统计信息创建一个通用计划。

当我使用上面的代码执行存储过程时,使用汇编的日期范围很窄或相当于一年的日期执行计划总是执行"索引扫描"操作。我看得出来参数嗅探被关闭,因为我知道date通常会创建一个索引查找操作。

我认为这是由于参数化。查询的第一个版本是参数化的,而第二个版本不是。"参数化查询需要较少的重新编译,而动态构建的查询需要非常频繁的编译和重新编译"(源)

对于使用参数构建的查询版本,将创建执行计划,然后重新使用:"如果SQL查询有参数,SQL Server将通过一个称为‘参数嗅探’的过程为它们创建一个定制的执行计划,以提高性能。此计划将被存储并重新使用,因为它通常是最佳执行计划"(源)。

最新更新