【问题标题】:Looping in cursors in oracle pl/sql在 oracle pl/sql 中循环游标
【发布时间】:2023-04-01 02:30:01
【问题描述】:

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

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;

这段代码sn-p是否会自动循环遍历table_student中的所有记录并在table_log中执行相应的更新?我需要在fetch语句之后添加一个循环吗?如果我在获取期间使用 Bulk collect 语句会有什么不同?

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

我遇到了一段 sn-p 代码,它使用循环作为游标,并在其中使用了 for 循环。以下是 sn-p 代码:

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 循环?

【问题讨论】:

  • 不,它们不会隐式获取所有行。您需要批量获取或循环,如documented
  • 另外,游标不存储查询结果。它stores information for processing a query。结果将流回给调用者。
  • 这么多嵌套的 BEGIN..END 块是代码异味。我不知道您在哪里“遇到了一段 sn-p 代码”,但我不相信这是良好实践的来源。
  • 实际上,在尝试修改我的问题以响应您的更新后,我改变了主意。此代码不会编译,也不会运行。它不仅不是良好实践的范例,而且是 shonky code 的典型代表。
  • @APC,我也开始重新格式化它,但失去了住在三层左右的意愿。

标签: oracle plsql


【解决方案1】:

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

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

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

  1. 打开光标
  2. 将数据提取到目标变量中
  3. 完成后,关闭光标

每次提取返回一行。要耗尽查询,您需要在循环中执行 fetch。这是这样做的详细方式:

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 的逐行处理;您引用的 sn-p 一次抓取 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:将两个游标合二为一。

【讨论】:

  • 有什么理由说明为什么使用了 for 循环?
  • 在游标循环中使用 for 循环是否会使代码变差?什么目的可能适合使用两个这样的循环?
猜你喜欢
  • 2012-03-27
  • 2012-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多