循环遍历带有更新字段条件的游标 [PLSQL]

Posted

技术标签:

【中文标题】循环遍历带有更新字段条件的游标 [PLSQL]【英文标题】:Looping through a cursor with a condition on an updated field [PLSQL] 【发布时间】:2017-09-18 18:10:53 【问题描述】:

我目前正在实现一个 PL/SQL 过程,它可以平衡两个列表中的值。

将此示例视为背景:

Rec_1 | 2 Rec_2 | 1 Rec_3 | 2
Rec_A | -1 Rec_B | -3 Rec_C | -2

我想逐个循环遍历所有这些值并尽可能多地结算,即第一次结算后 Rec_1 应该是 1,Rec_A 应该是 0。之后,Rec_1 将与 Rec_B 结算,这样它得到 1,Rec_B 得到 -2,以此类推。

我想使用两个游标来执行此操作,并在它自己的过程(或函数,如果有必要的话)中更新值,因为除了更新这个值之外,还有更多工作要做。

现在,这是我的挑战: 在结算发生后,我如何知道要获取哪个光标?

现在,我的这个函数的代码如下所示:

PROCEDURE SettleLists (
  ListNegV SYS_REFCURSOR,
  ListPosV SYS_REFCURSOR
) IS
  currentNegV TABLENAME%ROWTYPE;
  currentPosV TABLENAME%ROWTYPE;
BEGIN
  FETCH ListNegV INTO currentNegV;
  FETCH ListPosV INTO currentPosV;
  LOOP
    EXIT WHEN ListNegV%NOTFOUND;
    EXIT WHEN ListPosV%NOTFOUND;
    IF (currentNegV.NUMERICVALUE < 0)
    THEN
      IF (currentPosV.NUMERICVALUE > 0)
      THEN
        Settle(currentPosV, currentNegV);
      ELSE 
        FETCH ListPosV INTO currentPosV;
      END IF;
    ELSE 
      FETCH ListNegV INTO currentNegV;
    END IF;  
  END LOOP;
END;

在结算过程中,两条记录都会有更新。由于变量和游标值没有更新,这将产生一个无限循环。我也可以在数据库中更新记录时更新结算参数,但由于我不习惯游标,您可能会有更好的主意。

我可以认为光标是强类型的,如果这有什么不同的话。如果有比使用光标更好的方法,请随时提出建议。

最后,我在玩 SELECT FOR UPDATE 和 UPDATE WHERE CURRENT OF,但在将游标传递给介于两者之间的过程时,它似乎不起作用。如果有人对此有一些想法,我也将感谢您的帮助。

【问题讨论】:

【参考方案1】:

这是我要做的。我一定会添加一些cmets。

这在 Oracle 11Gr2 中运行良好,我希望即使在 7i 中也能正常运行 :)。

declare
    cursor c1 is 
        select 'REC_1' HEader,2 Val from dual
        union 
        select 'REC_2' HEader,1 Val from dual
        union 
        select 'REC_3' HEader,2 Val from dual;
    cursor c2 is
        select 'REC_A' HEader,-1 Val from dual
        union 
        select 'REC_B' HEader,-3 Val from dual
        union 
        select 'REC_C' HEader,-2 Val from dual;
    num_bal1 number;
    num_bal2 number;
    num_settle_amt number;
    rec_type_c1 c1%rowtype;
    rec_type_c2 c2%rowtype;
begin

    Open c1;
    open c2;
    fetch c1 into rec_type_c1;
    fetch c2 into rec_type_c2;
    num_bal1 := nvl(rec_type_c1.val,0);
    num_bal2 := rec_type_c2.val;
    Loop

        exit when c1%notfound or c2%notfound;
        Loop
            dbms_output.put_line('Processing ' || rec_type_c1.header || ' with ' || num_bal1);
            --In your example there are only +ve for 1 and -ve for 2. But if that is not correct, check for signs and next 3 statements
            num_settle_amt := least(abs(num_bal1), abs(num_bal2) );
            num_bal1 := num_bal1 - num_settle_amt;
            num_bal2 := num_bal2 + num_settle_amt;
            dbms_output.put_line('Setteled ' || num_settle_amt || ' of ' || rec_type_c1.header  || ' with ' || rec_type_c2.header );
            if num_bal1 = 0 then
                --Update in the table. It will not impact variable.
                fetch c1 into rec_type_c1;
                num_bal1 := nvl(rec_type_c1.val,0);
            end if;

            if num_bal2 = 0 then
                --Update in the table. It will not impact variable.
                fetch c2 into rec_type_c2;
                num_bal2 := nvl(rec_type_c2.val,0);
            end if;

        End loop;
    end loop;
    close c1;
    close c2;
end;

【讨论】:

感谢您花时间编写此代码。这很好用,我只是认为会有一些技术可以在不更新表和变量的情况下实现这一点。

以上是关于循环遍历带有更新字段条件的游标 [PLSQL]的主要内容,如果未能解决你的问题,请参考以下文章

sql 游标如何循环

SQL游标怎么循环更新

SQL中游标的使用--遍历数据逐行更新或删除:相当于for循环

使用游标 PLSQL 的性能

pl / sql如何使用带有游标的for循环进行更新操作

无法在 plsql 过程中找出循环游标