为啥使用声明的变量作为参数调用 UDF 比使用硬编码参数调用要快得多

Posted

技术标签:

【中文标题】为啥使用声明的变量作为参数调用 UDF 比使用硬编码参数调用要快得多【英文标题】:Why UDF call with declared variables as parameters is ways faster than call with hardcoded parameters为什么使用声明的变量作为参数调用 UDF 比使用硬编码参数调用要快得多 【发布时间】:2013-02-12 08:10:15 【问题描述】:

在我看来,我遇到了以下非常奇怪的问题。当我通过以下方式调用 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)))

运行4s,我得到如下执行计划: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】:

我相信这是由于参数化。您的查询的第一个版本是参数化的,而第二个不是。 “参数化的查询需要较少的重新编译,而动态构建的查询需要非常频繁地编译和重新编译”(source)

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

【讨论】:

以上是关于为啥使用声明的变量作为参数调用 UDF 比使用硬编码参数调用要快得多的主要内容,如果未能解决你的问题,请参考以下文章

UDF 可以访问调用 Pig 脚本中声明的参数或定义的值吗?

在Spark中如何使用UDO作为参数调用UDF以避免二进制错误

当 python 函数比它们快时,为啥我们使用 pyspark UDF? (注。不用担心 spark SQL 命令)

将 UDF 方法作为参数传递给 KSQL 中的其他 UDF

以整齐的方式将多列作为分组变量传递给 UDF

Java Spark-如何以多列作为参数调用UDF