在oracle pl / sql中循环游标

时间:2017-05-28 15:55:35

标签: oracle plsql

基本上,游标是内存区域,用于存储特定查询的结果。我有一个问题是游标是否隐式循环遍历所有记录?假设我编写了如下代码片段:

declare
    cursor cur_dum is 
        select name,class,enroll_id from table_student;
begin
    fetch cur_dum into the_name, the_class, the_enroll_id;

    update table_log set statement = the_name || '-'||'-'||to_char(the_enroll_id)
    where roll_id = the_enroll_id;

    close cur_dum;
end;

这个代码片段,没有任何明确的循环语句,会自动遍历table_student中的所有记录并在table_log中执行相应的更新吗?我需要在fetch语句之后添加一个循环吗?如果我在提取过程中使用批量收集语句会有什么不同?

从答案中,我得到明确说明循环是必要的。

我遇到了一段代码片段,它使用了一个游标循环,并在其中使用了一个for循环。以下是代码片段:

Cursor CurSquirell IS
  select Name,a_val,b_val,col_ID from table_temp;
BEGIN
    LoopCounter := 0;
    commit;
    LOOP
      FETCH CurSquirell BULK COLLECT INTO my_name,my_a_val,my_b_val,my_col_id LIMIT 1000;
        LoopCounter := LoopCounter + 1;
        FOR intIndex IN 1 .. my_col_id.COUNT LOOP
          counter := counter +1;
          BEGIN
            select t.tender_val,t.tender_pay, t.page_no, t.loc 
            into my_tender_val,my_tender_pay,my_page_no , my_loc
            from  bussiness_trans bt, tender_details t
            where t.account_no = bt.account_no
            and bt.external_id=my_col_id(intIndex)
            and trim(replace(t.tender_pay,'0',' ')) = trim(replace(a_val(intIndex),'0',' '))
            and bt.id_type=1;
            BEGIN
              select pp.lock_id into my__lock_id
              from pay_roll pp 
              where pp.pay_points= my_tender_pay
              and bt.id_type=5;
              BEGIN 
                 update tab_cross_exchange   tce
                 set      tce.cross_b_val = my_b_val(intIndex)
                 where    tce.lock_id = my_lock_id;
..............................sql statements...

...sql statements...

       end;
    end;
  end;

当代码循环已经被用于逐个浏览记录时,为什么使用了for loop?在什么情况下你需要在游标循环中使用这样的for loop?批量收集是否必须采取任何措施来强制使用For循环?

1 个答案:

答案 0 :(得分:1)

  

“游标是用于存储特定查询结果的内存区域”

不完全。游标是指向用于存储有关查询的信息的内存区域的指针。查询结果存储在内存的其他区域。

您使用的PL / SQL语法指定了一个定义查询的变量。要执行查询,您需要

  1. 打开光标
  2. 将数据提取到目标变量
  3. 完成后,关闭光标
  4. 每次提取都会返回一行。要耗尽查询,您需要在循环中执行提取。这是这样做的冗长方式:

    declare
        cursor cur_dum is 
            select name,class,enroll_id from table_student;
        rec_dum cur_dum%rowtype;
    begin
        open cur_dum;
        loop
            fetch cur_dum into rec_dum;
            exit when cur_dum%notfound;
            update table_log 
            set statement = rec_dum.name || '-'||'-'||to_char(rec_dum.enroll_id)
            where roll_id = rec_dum.enroll_id;
        end loop;
        close cur_dum;
    end;
    

    注意:这种显式游标符号的一个好处是我们可以定义一个类型为光标查询投影的变量(上面是rec_dum)。

    这与使用隐式光标表示法的逻辑相同:

    declare
        cursor cur_dum is 
    
        rec_dum cur_dum%rowtype;
    begin
        for rec_dum in (select name,class,enroll_id from table_student)
        loop
            update table_log 
            set statement = rec_dum.name || '-'||'-'||to_char(rec_dum.enroll_id)
            where roll_id = rec_dum.enroll_id;
        end loop;
    end;
    
      

    “批量收集是否必须做任何事情来强制使用For循环?”

    BULK COLLECT是允许我们使用一组记录填充嵌套表变量的语法,因此执行批量处理而不是上面说明的基本FETCH的逐行处理;您引用的代码段一次抓取一个包含1000条记录的子集,这在处理大量数据时是必需的,因为变量会填充私有(会话)内存而不是全局(共享内存)。您引用的代码非常差,尤其是因为FETCH ... BULK COLLECT INTO语句后面没有测试FETCH是否返回任何值。因为没有测试,后续代码将在运行时失败。

      

    “在游标循环中使用for循环会使代码变差吗?”

    不,不是自动的。例如,在进行批量处理时,我们可能经常做这样的事情:

    << batch_loop >>
    loop
        fetch dum_cur bulk collect into dum_recs limit 1000;
        exit when dum_recs.count() = 0;
        << row_loop >>
        for idx in dum_recs.first()..dum_recs.last() 
        loop
            do_something(dum_recs(idx));
        end loop row_loop;
    end loop batch_loop;
    

    但是,我们应该怀疑嵌套的CURSOR FOR循环。嵌套循环在像Java这样的3GL程序中很常见:

    for (int i = 1; i <= 5; i++) {
        for (int j = 1; j <= 10; j++) {  
    

    因此,熟悉这种编码风格的开发人员在迁移到PL / SQL时经常会遇到嵌套循环。但SQL是一种基于集合的范例。通常有更好的方法来实现该逻辑,例如JOIN:将两个游标合二为一。