1

私の SQL Server 2008 R2 データベースには、関数を呼び出すクエリがあります。

最近、このクエリの実行が非常に遅くなり始めました。ユーザー定義関数がすべてのクエリを妨げていることがわかりました。

この関数を単独で実行しようとしたところ、以前は 3 ~ 4 秒で実行されていましたが、完了するまでに 40 秒かかりました。そこで、関数内にあるコードを実行しようとしましたが、この 3 ~ 4 秒で実行されました。

関数のコードの実行にかかる時間が、関数自体の呼び出しよりもはるかに短い理由がわかりません。これをすべてSSMSでのみ試しました。

それが機能そのもの

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

そして、これは私がそれを呼び出す方法です

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

それ以外の場合は、別の dbserver でこの関数を実行しようとしました (この 2 つのサーバー間でレプリケーションが設定されています)。実行は非常に高速です。また、パラメーターなしで関数を呼び出し、それらを関数に直接設定すると、実行も高速になります。

4

2 に答える 2

3

おそらく古い統計。更新してみてください

UPDATE STATISTICS tbl_SI_CheckListRegistr
UPDATE STATISTICS tblArea
UPDATE STATISTICS tbl_SI_CheckList
于 2012-10-30T13:11:30.743 に答える
0

関数はまだコンパイルされておらず、最適な実行計画で実行されていません。関数を再コンパイルして、パフォーマンスを改善してください。

RECOMPILE : モジュールの実行後に新しいプランを強制的にコンパイル、使用、破棄します。モジュールの既存のクエリ プランがある場合、このプランはキャッシュに残ります。

sp_recompileを参照してください。

sp_recompile [ @objname = ] 'object'
于 2012-10-30T08:23:05.373 に答える