如何使用硬编码参数减少SQL标量函数的执行时间?

时间:2015-03-11 19:49:19

标签: sql sql-server sql-server-2008

我有一个现有的标量函数,对我来说效果非常好。使用此函数执行基本查询在不到1秒的时间内完成。以下是该功能的当前格式。

BEGIN  
    DECLARE @TEM VARCHAR(250);  
    SET @TEM = '';  

    SELECT  
        TOP 1 @TEM = ISNULL(B1_CHECKLIST_COMMENT,'')     
    FROM  
        BCHCKBOX  
    WHERE  
        B1_PER_ID1 = @PID1 AND  
        B1_PER_ID2 = @PID2 AND  
        B1_PER_ID3 = @PID3 AND  
        REC_STATUS = 'A' AND  
        SERV_PROV_CODE = 'Code' AND  
        BCHCKBOX.B1_CHECKBOX_GROUP = 'APPLICATION' AND  
        UPPER(B1_CHECKBOX_DESC) LIKE UPPER(@info_label) ;  

    RETURN(@TEM);  
END 

参数@ PID1,2,3在我的查询中总是相同的值,永远不会改变。因此,每当我在查询中使用该函数时,它的格式总是如下。

MyFunction(ID1,ID2,ID3,’Label’)

由于前三个参数的值总是ID1,2,3我想将它们硬编码到函数中,我只需要为函数键入以下内容。

MyFunction(‘Label’)

我尝试这样做的方法是将功能更改为以下内容。

BEGIN  
    DECLARE @TEM VARCHAR(250);  
    SET @TEM = '';  

    SELECT  
        TOP 1 @TEM = ISNULL(B1_CHECKLIST_COMMENT,'')      
    FROM  
        BCHCKBOX X  
    WHERE  
        X.B1_PER_ID1 = ID1 AND  
        X.B1_PER_ID2 = ID2 AND  
        X.B1_PER_ID3 = ID3 AND  
        X.REC_STATUS = 'A' AND  
        X.SERV_PROV_CODE = 'Code' AND  
        X.B1_CHECKBOX_GROUP = 'APPLICATION' AND  
        UPPER(B1_CHECKBOX_DESC) LIKE UPPER(@info_label) ;  

    RETURN(@TEM);  
END  

在修订后的函数中,我将表别名,然后只使用实际值(ID1,2,3)而不是参数。该功能就像以前一样工作。但是,对于完全相同的查询,执行时间已增加到大约2分钟,而且查询时间不到1秒。

为什么硬编码值会大大增加执行时间?是否有更好的方法来格式化此函数以实现与原始函数中注意到的相同的执行时间?

提前致谢!

下面编辑的原始帖子,用于说明基于@ AXIMIM和@SeanLange推荐的编辑完成编辑后的功能

ALTER  FUNCTION  [dbo].[FN_GetASIValue](@info_label  VARCHAR(250)) RETURNS VARCHAR (500)  AS    
    BEGIN 
        DECLARE
            @TEM VARCHAR(250),
            @ID1 VARCHAR(20),
            @ID2 VARCHAR(20),
            @ID3 VARCHAR(20);

        SELECT
            @TEM = '',
            @ID1 = 'B1_PER_ID1',
            @ID2 = 'B1_PER_ID2',
            @ID3 = 'B1_PER_ID3'

        SELECT
            TOP 1 @TEM = ISNULL(B1_CHECKLIST_COMMENT,'')

        FROM
            BCHCKBOX
        WHERE
            B1_PER_ID1 = @ID1 AND
            B1_PER_ID2 = @ID2 AND
            B1_PER_ID3 = @ID3 AND
            REC_STATUS = 'A' AND
            SERV_PROV_CODE = 'Code' AND
            B1_CHECKBOX_GROUP = 'APPLICATION'
        ORDER BY  B1_CHECKBOX_IND;
        RETURN(@TEM);
    END 

使用以下调用执行此函数在不到一秒的时间内完成,并返回与原始函数相同的行数。

FN_GetASIValue('location')

但是,结果中位置列的值为空。原始函数确实在位置列中返回文本。

1 个答案:

答案 0 :(得分:0)

我没有足够的声誉来评论你的问题,所以在这里你应该看看。

新版本可能有两种原因可能很慢。

1)硬编码值与表BCHCKBOX中的类型不同,SQL对所有记录执行隐式转换。此转换可防止索引正确使用,也是额外的开销。

2)由于没有更多参数,查询不再使用先前的执行计划。需要创建一个新的执行计划。

作为“修复”问题的建议,您可以简单地将硬编码值声明为函数体中的变量。通过这种方式,您可以避免这些问题,并且仍然可以获得仅具有一个参数功能的好处。

这就是我的意思。 (假设类型为VARCHAR(50)。请使用与参数中相同的类型)

BEGIN  
    DECLARE @TEM VARCHAR(250),
            @ID1 VARCHAR(50),  
            @ID2 VARCHAR(50),  
            @ID3 VARCHAR(50)  

    SELECT  @TEM = '',
            @ID1 = 'ID1',
            @ID2 = 'ID2',
            @ID3 = 'ID3'

    SELECT  
        TOP 1 @TEM = ISNULL(B1_CHECKLIST_COMMENT,'')      
    FROM  
        BCHCKBOX X  
    WHERE  
        X.B1_PER_ID1 = @ID1 AND  
        X.B1_PER_ID2 = @ID2 AND  
        X.B1_PER_ID3 = @ID3 AND  
        X.REC_STATUS = 'A' AND  
        X.SERV_PROV_CODE = 'Code' AND  
        X.B1_CHECKBOX_GROUP = 'APPLICATION' AND  
        UPPER(B1_CHECKBOX_DESC) LIKE UPPER(@info_label) ;  

    RETURN(@TEM);  
END  

另外,你应该看看Sean Lange的评论。他指出了应该考虑的功能中的其他一些问题。