如何用游标获取唯一值?解决游标重复、慢问题及唯一ID求和
问题分析与解决
嘿,我看到你作为游标新手,在处理数据时遇到了执行慢、结果重复的问题,还想知道怎么对每个唯一ID对应的值求和。咱们一步步拆解问题,解决这些坑。
你的代码里的核心错误
先看你写的存储过程,两个关键问题导致了当前的异常:
- 游标定义语法错误:你错误地用了字符串拼接的方式写SELECT语句(
'SELECT DISTINCT '''+ bpm_referenceno...),这完全没必要,还会引发语法问题。游标可以直接关联常规的SELECT查询,不需要把它当成字符串处理。另外,你已经用了GROUP BY bpm_referenceno,这已经保证每个bpm_referenceno只返回一行,DISTINCT是多余的。 - 循环内未再次执行FETCH:你只在循环开始前执行了一次
FETCH NEXT,循环体里没有再次获取下一行数据,这会导致无限循环——一直打印第一行的结果,看起来就是“结果重复”,同时死循环会让执行速度慢得离谱。
修正后的存储过程
先把代码改成能正常工作的版本:
CREATE PROCEDURE test1 AS BEGIN -- 声明变量 DECLARE @wi_name NVARCHAR(50); DECLARE @date DATETIME; -- 定义游标:直接关联正确的SELECT查询,GROUP BY已保证唯一行 DECLARE fetch_disb_date CURSOR FOR SELECT bpm_referenceno, MAX(callexecutiondate) FROM BM_RLOS_DisbursementCallsGrid WHERE calldescription='MG Contract Creation' AND callstatus='SUCCESS' GROUP BY bpm_referenceno; -- 打开游标 OPEN fetch_disb_date; -- 获取第一行数据 FETCH NEXT FROM fetch_disb_date INTO @wi_name, @date; -- 循环处理每一行 WHILE (@@FETCH_STATUS = 0) BEGIN -- 打印当前行的数据(优化格式更易读) PRINT 'bpm_referenceno: ' + @wi_name; PRINT 'Max Execution Date: ' + CONVERT(VARCHAR(20), @date, 120); PRINT '------------------------'; -- 必须获取下一行,否则会无限循环 FETCH NEXT FROM fetch_disb_date INTO @wi_name, @date; END; -- 关闭并释放游标(必须做,避免资源泄漏) CLOSE fetch_disb_date; DEALLOCATE fetch_disb_date; END
关于“对每个唯一ID对应的游标值进行求和”
假设你想对每个bpm_referenceno对应的某个数值字段(比如金额、数量)求和,有两种常用方式:
方式1:在SELECT查询里直接计算(推荐,效率更高)
SQL是基于集合的语言,直接用GROUP BY加SUM()就能完成,完全不需要游标:
SELECT bpm_referenceno, MAX(callexecutiondate) AS max_callexecutiondate, SUM(your_numeric_column) AS id_total -- 替换成你要求和的字段名 FROM BM_RLOS_DisbursementCallsGrid WHERE calldescription='MG Contract Creation' AND callstatus='SUCCESS' GROUP BY bpm_referenceno;
如果一定要用游标,把这个查询放到游标定义里,再声明对应的变量接收求和结果即可。
方式2:在游标循环里累加总和
如果需要在游标处理过程中动态累加所有ID的总和,可以声明一个累加变量:
CREATE PROCEDURE test1_with_sum AS BEGIN DECLARE @wi_name NVARCHAR(50); DECLARE @date DATETIME; DECLARE @id_total DECIMAL(18,2); -- 存储单个ID的求和结果 DECLARE @overall_total DECIMAL(18,2) = 0; -- 存储所有ID的总累加值 DECLARE fetch_disb_date CURSOR FOR SELECT bpm_referenceno, MAX(callexecutiondate), SUM(your_numeric_column) AS id_total FROM BM_RLOS_DisbursementCallsGrid WHERE calldescription='MG Contract Creation' AND callstatus='SUCCESS' GROUP BY bpm_referenceno; OPEN fetch_disb_date; FETCH NEXT FROM fetch_disb_date INTO @wi_name, @date, @id_total; WHILE (@@FETCH_STATUS = 0) BEGIN PRINT 'bpm_referenceno: ' + @wi_name; PRINT 'Max Execution Date: ' + CONVERT(VARCHAR(20), @date, 120); PRINT 'ID Total: ' + CONVERT(VARCHAR(20), @id_total); PRINT '------------------------'; -- 累加所有ID的总和 SET @overall_total = @overall_total + @id_total; FETCH NEXT FROM fetch_disb_date INTO @wi_name, @date, @id_total; END; -- 打印最终总累加值 PRINT 'Overall Total: ' + CONVERT(VARCHAR(20), @overall_total); CLOSE fetch_disb_date; DEALLOCATE fetch_disb_date; END
额外建议:尽量避免游标(如果可以的话)
游标是逐行操作,效率远低于SQL的集合操作。如果你的需求只是获取每个ID的最大日期、求和,直接用GROUP BY的SELECT查询就能完成,完全不需要游标。只有当你需要对每一行进行复杂逻辑处理(比如调用其他存储过程、逐行更新数据)时,再考虑使用游标。
内容的提问来源于stack exchange,提问作者Md Kamran Azam
相关产品推荐
相关产品推荐

