我正在使用函数更新到一列,比如
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
结束
提前致谢。
答案 0 :(得分:0)
当您尝试更新500k行时,似乎您的执行计划发生了变化。
您可以尝试在forceseek
子句上设置from
提示,以强制使用seek而不是扫描。
此外,WHILE (@iRowCount > @iLoopCount)
应替换为if exists
,因为您基本上会检查结果表中的某些条件,并且您需要尽早返回。
我看到你在任何地方使用nolock
提示允许脏读,你可以在调用存储过程中set isolation level read uncommitted
并删除所有这些;或者考虑将数据库更改为set read_committed_snapshot on
以避免锁定。
顺便说一句,SQL Server中的标量函数调用非常昂贵,因此如果在调用函数的循环中发生了大量更新/选择,则必须尽可能避免使用函数。