Why calling a function takes much more time than direct execution of function code

假装没事ソ 提交于 2021-01-28 23:00:34

问题


In my SQL Server 2008 R2 database, I have a query which calls a function.

Lately this query has started to execute very slowly. I found that the user-defined function is hampering all the query.

I tried to execute this function alone and it took 40 seconds to finish, while previously it executed in 3-4 seconds. So I tried to execute code that is inside of function and executed in this 3-4 seconds.

I cannot understand why executing code of function takes far fewer time than calling function itself. I tried all this in SSMS only.

That's the function itself

ALTER FUNCTION [dbo].[fn_SI_GetMark] (@AREA_ID int, @BD datetime, @ED datetime)  
RETURNS decimal(24, 2) AS  
BEGIN 



Declare @mon int;
set @Mon = 1;
if(DateDiff(day, @BD, @ED) > 40)
begin

declare @q1sb datetime; set @q1sb = Convert(datetime, '01.01.'+Convert(nvarchar(4), Year(@BD)));
declare @q1eb datetime; set @q1eb = Convert(datetime, '01.04.'+Convert(nvarchar(4), Year(@BD)));
declare @q2sb datetime; set @q2sb = Convert(datetime, '01.04.'+Convert(nvarchar(4), Year(@BD)));
declare @q2eb datetime; set @q2eb = Convert(datetime, '01.07.'+Convert(nvarchar(4), Year(@BD)));
declare @q3sb datetime; set @q3sb = Convert(datetime, '01.07.'+Convert(nvarchar(4), Year(@BD)));
declare @q3eb datetime; set @q3eb = Convert(datetime, '01.10.'+Convert(nvarchar(4), Year(@BD)));
declare @q4sb datetime; set @q4sb = Convert(datetime, '01.10.'+Convert(nvarchar(4), Year(@BD)));
declare @q4eb datetime; set @q4eb = Convert(datetime, '01.01.'+(Convert(nvarchar(4), Year(@BD) + 1)));
if((@BD >= @q1sb) and (@BD < @q1eb))
begin
    set @BD = @q1sb;
end
else if((@BD >= @q2sb) and (@BD < @q2eb))
begin
    set @BD = @q2sb;
end
else if((@BD >= @q3sb) and (@BD < @q3eb))
begin
    set @BD = @q3sb;
end
else if((@BD >= @q4sb) and (@BD < @q4eb))
begin
    set @BD = @q4sb;
end



declare @q1se datetime; set @q1se = Convert(datetime, '01.01.'+Convert(nvarchar(4), Year(@ED)));
declare @q1ee datetime; set @q1ee = Convert(datetime, '01.04.'+Convert(nvarchar(4), Year(@ED)));
declare @q2se datetime; set @q2se = Convert(datetime, '01.04.'+Convert(nvarchar(4), Year(@ED)));
declare @q2ee datetime; set @q2ee = Convert(datetime, '01.07.'+Convert(nvarchar(4), Year(@ED)));
declare @q3se datetime; set @q3se = Convert(datetime, '01.07.'+Convert(nvarchar(4), Year(@ED)));
declare @q3ee datetime; set @q3ee = Convert(datetime, '01.10.'+Convert(nvarchar(4), Year(@ED)));
declare @q4se datetime; set @q4se = Convert(datetime, '01.10.'+Convert(nvarchar(4), Year(@ED)));
declare @q4ee datetime; set @q4ee = Convert(datetime, '01.01.'+(Convert(nvarchar(4), Year(@ED) + 1)));
if((@ED >= @q1se) and (@ED <= @q1ee))
begin
    set @ED = @q1ee;
end
else if((@ED >= @q2se) and (@ED <= @q2ee))
begin
    set @ED = @q2ee;
end
else if((@ED >= @q3se) and (@ED <= @q3ee))
begin
    set @ED = @q3ee;
end
else if((@ED >= @q4se) and (@ED <= @q4ee))
begin
    set @ED = @q4ee;
end


set @Mon = datediff(month, @BD, @ED) / 3;

end


declare @i int;

DECLARE @Mark decimal(24, 2); SET @Mark = 0;
declare @count int; SET @count = 0;

DECLARE @AREA_PATH nvarchar(max), @SI_CheckListId int, @SI_CheckListTitle nvarchar(max), @SI_CheckListCreatedBy int;


DECLARE @Mark2 decimal(24, 2); set @Mark2 = 0;
declare @count2 int; SET @count2 = 0;



DECLARE @areaIdStr nvarchar(max);
set @areaIdStr = convert(nvarchar(max), @AREA_ID);


DECLARE db_cursor_rights2 CURSOR
for
SELECT     tbl_SI_CheckList.SI_CheckListId
FROM         tblArea INNER JOIN
                  tbl_SI_CheckList ON tblArea.AREA_ID = tbl_SI_CheckList.AreaId
WHERE ('%/'+tblArea.AREA_PATH+'/%' like '%/'+@areaIdStr+'/%') 
and (((tbl_SI_CheckList.SI_CheckListIsDeleted <> 1) and (tbl_SI_CheckList.SI_CheckListDateCreated <= @ED))
    or  
    ((tbl_SI_CheckList.SI_CheckListIsDeleted = 1) and (tbl_SI_CheckList.SI_CheckListDateDeleted is not null) and (tbl_SI_CheckList.SI_CheckListDateDeleted >= @BD) and (tbl_SI_CheckList.SI_CheckListDateCreated <= @ED)))


OPEN db_cursor_rights2;  
FETCH NEXT FROM db_cursor_rights2
INTO @SI_CheckListId;
WHILE @@FETCH_STATUS = 0
BEGIN   


    set @i = 1
    while @i <= @Mon
    begin
        set @Mark2 = 0
        set @count2 = 0

        SELECT     @Mark2 = @Mark2 + Mark
        FROM       tbl_SI_CheckListRegistr
        WHERE     (SI_CheckListId = @SI_CheckListId) and ((tbl_SI_CheckListRegistr.DateCreated >= @BD) and (tbl_SI_CheckListRegistr.DateCreated < @ED) and (tbl_SI_CheckListRegistr.IsDeleted <> 1))

        if(@Mark2 is not null)
        begin 
            set @Mark = @Mark + @Mark2;
        end
        set @i = @i + 1;    

        SELECT     @count2 = count(Mark)
        FROM       tbl_SI_CheckListRegistr
        WHERE     (SI_CheckListId = @SI_CheckListId) and ((tbl_SI_CheckListRegistr.DateCreated >= @BD) and (tbl_SI_CheckListRegistr.DateCreated < @ED) and (tbl_SI_CheckListRegistr.IsDeleted <> 1))

        if(@count2 = 0)
        begin 
            set @count2 = @count2 + 1;
        end 

        set @count = @count + @count2;      

    end

    FETCH NEXT FROM db_cursor_rights2
    INTO @SI_CheckListId;

END
CLOSE db_cursor_rights2;
DEALLOCATE db_cursor_rights2;

if(@count = 0)
begin
set @count = 1;
end

set @Mark = round(@Mark/@count, 2)

RETURN (@Mark)


END

and this is how i try to call it

DECLARE @AREA_ID int; SET @AREA_ID=1;
DECLARE @BD datetime, @ED datetime;
SET @BD=cast('2012-10-01' AS DATETIME);
SET @ED=cast('2012-11-01' AS DATETIME);
DECLARE @MArk decimal(24,2);
set @Mark = (select dbo.fn_SI_GetMark(@AREA_ID, @BD, @ED));
PRINT @Mark

Else one thing, i tried to execute this function on another dbserver (there is replication set up between this two servers) and it executes very fast. And another one observation if i call function without parameters, set them in function directly, it is also executes fast.


回答1:


Probably old statistics. Try to update

UPDATE STATISTICS tbl_SI_CheckListRegistr
UPDATE STATISTICS tblArea
UPDATE STATISTICS tbl_SI_CheckList



回答2:


Function is yet compiled and is not runing with optimum execution plan. Try to recompile function to improve performance.

RECOMPILE: Forces a new plan to be compiled, used, and discarded after the module is executed. If there is an existing query plan for the module, this plan remains in the cache.

See sp_recompile:

sp_recompile [ @objname = ] 'object'


来源:https://stackoverflow.com/questions/13135069/why-calling-a-function-takes-much-more-time-than-direct-execution-of-function-co

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!