我有一个存储过程,它使用FAST_FORWARD游标按时间顺序循环一组~300k记录,并根据许多运行变量和标志的状态将它们分配给声明集,部分实现为表变量。关于如何做这个基于集合的我已经考虑了很多,我不能这样做。所以我坚持使用光标方法,需要优化此代码。
我注意到前10%的进度加载和处理非常快(2000行/秒),接近20%的进度已经减慢到大约300行/秒,最后它已经放慢到大约60行/秒。
IMO这可能是由于以下四个原因:
或者我的百分比计数器坏了:
SET @curprogress = @curprogress + 1
IF (@curprogress - ((@totprogress / 100) * (FLOOR(@curprogress * 100 / @totprogress)))) BETWEEN 0 AND 1 BEGIN
SET @msg = CAST(FLOOR(@curprogress * 100 / @totprogress) AS VARCHAR)
RAISERROR('%s%s', 0, 1, @msg, '%...') WITH NOWAIT;
END
有没有人知道要查找什么以及如何加快查询速度?
我的代码的符号表示:
WHILE....
-- Fetch new record to be assigned to one of the open declaration sets
FETCH NEXT INTO @row_field1, @row_field2....
IF (@flag2 = 1) AND ((@flag1 = 0) OR (@row_field1 <> @prevrow_field1))
BEGIN
-- Logging info: we are closing a child declaration set
INSERT INTO @logtable SELECT '--> LOG MESSAGE'
INSERT INTO @logtable
SELECT format_message(@row_field1, @calc_field2, field3...)
FROM @runningtable_sub S LEFT JOIN @runningtable_main M ON S.MainID = M.ID
-- Update enddate of parent
UPDATE M SET M.enddate = DATEADD(day,365,S.enddate)
FROM @runningtable_sub S
LEFT JOIN @runningtable_main M
ON S.MainID = M.ID
-- close and save child
INSERT INTO outputtable_main
SELECT @field1, COALESCE(Z.Field1,'NULL'), S.startdate, S.enddate,
M.Startdate, M.Enddate
FROM @runningtable_sub S
LEFT JOIN @runningtable_main M ON S.MainID = M.ID
-- delete child from running table
DELETE FROM @runningtable_sub WHERE S.enddate < @curdate
END
答案 0 :(得分:0)
我可以想到很多原因这种情况正在放缓,但如果不知道数据的基数,就很难缩小范围。
随机观察:
要自己解决一些问题,请添加检测:
DECLARE @now DATETIME, @duration INT, @rowcount INT
WHILE....
-- Fetch new record to be assigned to one of the open declaration sets
FETCH NEXT INTO @row_field1, @row_field2....
IF (@flag2 = 1) AND ((@flag1 = 0) OR (@row_field1 <> @prevrow_field1))
BEGIN
PRINT '---------------'
-- Logging info: we are closing a child declaration set
INSERT INTO @logtable SELECT '--> LOG MESSAGE'
SET @now = GETDATE()
INSERT INTO @logtable
SELECT format_message(@row_field1, @calc_field2, field3...)
FROM @runningtable_sub S LEFT JOIN @runningtable_main M ON S.MainID = M.ID
SELECT @rowcount = @@ROWCOUNT, @duration = DATEDIFF(ms,@now,GETDATE)
RAISERROR('%i row(s) inserted into @logtable, %i milliseconds',-1,-1,@rowcount,@duration) WITH NOWAIT
-- Update enddate of parent
SET @now = GETDATE()
UPDATE M SET M.enddate = DATEADD(day,365,S.enddate)
FROM @runningtable_sub S
LEFT JOIN @runningtable_main M
ON S.MainID = M.ID
SELECT @rowcount = @@ROWCOUNT, @duration = DATEDIFF(ms,@now,GETDATE)
RAISERROR('%i row(s) updated in @runningtable_main, %i milliseconds',-1,-1,@rowcount,@duration) WITH NOWAIT
-- close and save child
SET @now = GETDATE()
INSERT INTO outputtable_main
SELECT @field1, COALESCE(Z.Field1,'NULL'), S.startdate, S.enddate,
M.Startdate, M.Enddate
FROM @runningtable_sub S
LEFT JOIN @runningtable_main M ON S.MainID = M.ID
SELECT @rowcount = @@ROWCOUNT, @duration = DATEDIFF(ms,@now,GETDATE)
RAISERROR('%i row(s) inserted into outputtable_main, %i milliseconds',-1,-1,@rowcount,@duration) WITH NOWAIT
-- delete child from running table
SET @now = GETDATE()
DELETE FROM @runningtable_sub WHERE S.enddate < @curdate
SELECT @rowcount = @@ROWCOUNT, @duration = DATEDIFF(ms,@now,GETDATE)
RAISERROR('%i row(s) deleted from @runningtable_sub, %i milliseconds',-1,-1,@rowcount,@duration) WITH NOWAIT
END
如果你包含了你的整个代码(包括游标声明),而不仅仅是一个符号摘录,那么这里的某个人可能会为你做一些更高效的工作和/或一起避免游标。