如何替换嵌套游标?

时间:2013-08-05 13:55:11

标签: sql-server cursor

我正在为我们运营的网站编写成就模块。这段特殊的代码将在给定时间段结束时在SQL Server上运行,以奖励成就(下次访问该站点时将通过客户端通知)。

因此,我需要了解所有团队和所有“最终”成就(独立于团队)。每项成就都有一个或多个必须满足的规则。一旦我确定成就通过,我就会将成就奖励给该团队的所有参与者。

我通过游标处理这个问题,但我得到了一个错误,所以当我去google问题时,我在论坛上得到了无数的链接“你是DUMB $&#$为什么你使用游标”(用来解释) 。我想在解决问题的同时,我也可以使用基于集合的方法替换我所拥有的(如果可能的话)。

我发现的替代示例都是在表上使用单个嵌套循环的基本更新方案,这些表具有彼此重复的键,老实说,我不会考虑在这些场景中使用游标开始。

我在下面的内容是完全在语法上正确,但我可以通过玩游戏来解决这个问题。这应该清楚地说明我想要完成的任务,但是:

declare @TimeframeID int;
declare @AchievementID int;
declare @q1 int;
declare @q2 int;
declare @TeamID int;
declare @TotalReg int;  
declare @TotalMin int;
declare @AvgMin decimal(10, 2);
declare @AggType varchar(50);
declare @Pass bit = 1;
declare @Email varchar(50);
declare @ParticipantID int;

select @TimeframeID = MAX(TimeframeID) from Timeframes 
where IsActive = 1;

declare team_cur CURSOR FOR
select 
    t.TeamID, 
    (select COUNT(1) from Registrations r 
        where r.TeamID = t.TeamID and r.TimeframeID = @TimeframeID) TotalReg,
    (select SUM(Minutes) from Activities a 
        inner join Registrations r on r.RegistrationID = a.RegistrationID
        where r.TeamID = t.TeamID and r.TimeframeID = @TimeframeID) TotalMin
from Teams t
where Active = 1
group by TeamID;

declare ach_cur CURSOR FOR
        select AchievementID from luAchievements
        where TimeframeID = @TimeframeID and AchievementType = 'End';

declare rule_cur CURSOR for
        select Quantity1, Quantity2, AggregateType
        from AchievementRule_Links arl
        inner join luAchievementRules ar on ar.RuleID = arl.RuleID 
        where arl.AchievementID = @AchievementID;

open team_cur;

fetch next from team_cur
into @TeamID, @TotalReg, @TotalMin;

while @@FETCH_STATUS = 0
begin

    open ach_cur;

    fetch next from ach_cur
    into @AchievementID;

    while @@FETCH_STATUS = 0
        begin
            open rule_cur;

            fetch next from rule_cur
            into @q1, @q2, @AggType;

            while @@FETCH_STATUS = 0
                begin
                    if @AggType = 'Total'
                    begin
                        if @q1 > @TotalReg 
                        begin
                            set @Pass = 0;
                        end
                    end
                    else if @AggType = 'Average'
                    begin
                        print 'do this later; need to get specs';
                    end
                fetch next from rule_cur
                into @q1, @q2, @AggType;
            end

            close rule_cur;
            deallocate rule_cur;

            -- if passed, award achievement to all team members
            if @Pass = 1
            begin
                declare reg_cursor cursor for
                select max(p.Email) from Participants p
                inner join Registrations reg on reg.ParticipantID = p.ParticipantID
                where reg.TeamID = @TeamID;

                open reg_cursor;

                fetch next from reg_cursor
                into @Email;

                while @@FETCH_STATUS = 0
                begin
                    exec ProcessAchievement @AchievementID, @Email, 0;

                    fetch next from reg_cursor
                    into @Email;
                end

                close reg_cursor;
                deallocate reg_cursor;

                -- award achievement to team
                exec ProcessTeamAchievement @AchievementID, @TeamID;
            end

            fetch next from ach_cur
            into @AchievementID;        
        end

    close ach_cur;
    deallocate ach_cur;

    fetch next from team_cur
    into @TeamID, @TotalReg, @TotalMin;
end

close team_cur;
deallocate team_cur;

我在这里做的是否有基于集合的替代方案?我需要补充的是,这是针对一小组记录运行的,因此性能不是我关注的问题;未来庞大更新的最佳实践是。

1 个答案:

答案 0 :(得分:1)

要使这个基于集合,您需要修复正在调用的过程,以便它们具有表值参数或proc中的代码连接到您要处理的记录的表。在第二种情况下,您可以将它们标记为已处理,也可以在完成后将其删除。