如何在SQL中使函数执行得更快?

时间:2014-03-18 06:13:15

标签: sql-server-2008 function database-performance

我正在使用函数更新到一列,比如

DetailedStatus = dbo.fn_GetProcessStageWiseStatus(PR.ProcessID, PR.ProcessRunID, getdate()) 

此行中连续更新了500,000条记录。它就像一个循环

因此,对于少数记录使用此函数,它的执行速度很快,但当执行它的500,000条记录变得很慢时......

如何使用许多记录更快地执行此操作?

要采取的措施或任何拆分措施?

功能:

             CREATE FUNCTION [dbo].[fn_GetProcessStageWiseStatus]    
       (    
       @ProcessID INT    
        ,@ProcessRunID INT    
        ,@SearchDate SMALLDATETIME    
       )    
          RETURNS VARCHAR(100)    
         AS    

      BEGIN    
         DECLARE      
             @iLoopCount SMALLINT    
            ,@iRowCount SMALLINT    
            ,@StepProgress VARCHAR(100)    
            ,@StepCount SMALLINT    

     IF EXISTS(    
              SELECT TOP 1 1    
              FROM dbo.Step S WITH(NOLOCK)    
              JOIN dbo.vw_FileGroup FG    
                    ON S.FileConfigGroupID = FG.FileConfigGroupID    
              WHERE S.ProcessID = @ProcessID    
              AND S.Active = 1    
              AND FG.FileConfigGroupActive = 1    
              AND FG.Direction = 'Inbound'    
              )    
  BEGIN    
        SET @StepProgress = 'Not Received'    
  END    
  ELSE    
  BEGIN    
        SET @StepProgress = 'Not Started'    
  END    

  DECLARE @StepRunDetailsTable TABLE    
  (    
        KeyNo INT IDENTITY(1,1)    
        ,StepID INT    
        ,StepStartTime SMALLDATETIME    
        ,StepEndTime SMALLDATETIME    
        ,SourceEnv VARCHAR(100)    
        ,DestEnv VARCHAR(100)    
  )    

  INSERT INTO @StepRunDetailsTable     
  SELECT    
        S.StepID    
        ,MAX(isnull(SR.StepStartTime, '06/06/2079'))    
        ,MAX(isnull(SR.StepEndTime, '06/06/2079'))    
        ,isnull(SENV.EnvironmentName, '')    
        ,isnull(DENV.EnvironmentName, '')    
  FROM dbo.ProcessRun PR WITH(NOLOCK)    
  JOIN dbo.StepRun SR WITH(NOLOCK)    
        ON SR.ProcessRunID = PR.ProcessRunID    
  JOIN dbo.vw_StepHierarchy SH    
        ON SR.StepID = SH.StepID    
        AND SH.Active = 1    
  JOIN dbo.Step S WITH(NOLOCK)    
        ON SH.StepID = S.StepID    
  JOIN dbo.WorkFlow WF WITH(NOLOCK)    
        ON S.WorkFlowID = WF.WorkFlowID    
        AND WF.Active = 1           
  JOIN dbo.Environment SENV WITH(NOLOCK)    
        ON SENV.EnvironmentID = WF.SourceEnvironmentID    
        AND SENV.Active = 1               
  JOIN dbo.Environment DENV WITH(NOLOCK)    
        ON DENV.EnvironmentID = WF.DestinationEnvironmentID    
        AND DENV.Active = 1    
  WHERE PR.ProcessRunID = @ProcessRunID    
  GROUP BY S.StepID, SENV.EnvironmentName, DENV.EnvironmentName, SH.StepOrder    
  ORDER BY SH.StepOrder ASC    

  SELECT @StepCount = COUNT(*)    
  FROM dbo.ProcessRun PR WITH(NOLOCK)    
  JOIN dbo.Step S WITH(NOLOCK)    
        ON PR.ProcessID = S.ProcessID    
        AND PR.ProcessRunID = @ProcessRunID    
        AND S.Active = 1    

  SELECT @iRowCount = COUNT(DISTINCT StepID) FROM @StepRunDetailsTable    

  SET @iLoopCount = 0      

  WHILE (@iRowCount > @iLoopCount)      
  BEGIN      
        SET @iLoopCount = @iLoopCount + 1    

        SELECT    
              @StepProgress =     
                    CASE    
                          --WHEN @SearchDate BETWEEN StepStartTime AND StepEndTime     
                          WHEN @SearchDate >= StepStartTime AND  @SearchDate <= StepEndTime     
                                THEN DestEnv + ' Load in Progress'    
                          WHEN @SearchDate > StepEndTime AND @iLoopCount < @StepCount    
                                THEN 'Waiting on next step - Loaded to ' + DestEnv    
                          WHEN @SearchDate > StepEndTime AND @iLoopCount = @StepCount    
                                THEN 'Completed'    
                          WHEN @SearchDate < StepStartTime AND @iLoopCount = 1    
                                THEN 'Load Not Started'    
                          ELSE @StepProgress    
              END    

        FROM @StepRunDetailsTable    
        WHERE KeyNo = @iLoopCount    
  END    

  RETURN @StepProgress        

结束

提前致谢。

1 个答案:

答案 0 :(得分:0)

当您尝试更新500k行时,似乎您的执行计划发生了变化。 您可以尝试在forceseek子句上设置from提示,以强制使用seek而不是扫描。

此外,WHILE (@iRowCount > @iLoopCount)应替换为if exists,因为您基本上会检查结果表中的某些条件,并且您需要尽早返回。

我看到你在任何地方使用nolock提示允许脏读,你可以在调用存储过程中set isolation level read uncommitted并删除所有这些;或者考虑将数据库更改为set read_committed_snapshot on以避免锁定。

顺便说一句,SQL Server中的标量函数调用非常昂贵,因此如果在调用函数的循环中发生了大量更新/选择,则必须尽可能避免使用函数。