重新使用游标重新打开

时间:2016-12-14 10:21:55

标签: oracle loops cursor proc

我有一张员工的桌子,每个员工都有很少的账单。 我宣布2个游标,一个用于所有员工(不同),第二个游标用于一个员工的所有账单。现在,打开所有员工的第一个光标,获取一个,打开第二个光标(基于第一个光标的员工)和员工的所有账单。要为所有员工重用第二个游标,我打开并关闭每个员工的第二个游标。这件事花了很多时间。如何重用第二个游标而不是重新打开或任何好主意?

Pro * C中的部分代码:

if(id->fa_addr)
   // print statements

}

nis_rad和sec_nis是employee_id(主键)。每个nis_rad都有很少的账单f_fact(账单)

处理10000 nis_rad花费30分钟,28-29分钟用于重新打开第二个光标(c2)

UP。删除了之前的示例

3 个答案:

答案 0 :(得分:0)

我编辑了一个答案。

除非将所有结果缓存在内存中,否则无法再次打开日期而无法重新使用日期。当你读取已经指向下一个的记录时,光标就像指向数据的指针,所以你无法回来。

代码中的问题是使用SQL命令方式。您不应该将所有记录提取到您的应用程序,然后应用逻辑。考虑如何编写一个只返回您需要处理的记录的查询。执行2-3个查询可能会更快,这些查询将返回代码的每个部分的记录,而不是拉取所有数据,然后检查应用程序中的逻辑。

答案 1 :(得分:0)

我会说打开显式cursor,让oracle使用implicit游标并使用简单循环来满足您的要求:

declare

begin
for rec in ( select employee_id
             from employees )
  Loop                   
          for rcrd in (Select bill from employee_bill 
                      where employee_id = rec.employee_id)
           loop

            /***Process your bills***/

           end loop;                           

  end loop;

end; 

答案 2 :(得分:0)

你的代码的问题在于它正在进行大量的上下文切换,所以难怪它的速度很慢。

据我所知,您的代码正在执行以下操作:

  1. 在Pro * C中,转到数据库
  2. 在数据库中,打开第一个光标
  3. 返回Pro * C,向数据库询问光标中的行
  4. 数据库发回一行
  5. 在Pro * C中,转到数据库
  6. 在数据库中,打开第二个光标,传入第一个光标的值
  7. 在Pro * C中,向数据库询问第二个光标
  8. 中的行
  9. 数据库发回一行
  10. 在Pro * C中,要求数据库根据第二个光标的结果更新行
  11. 数据库更新行。
  12. 重复步骤7-10,直到没有其他行要从光标2
  13. 获取
  14. 重复步骤3.11,直到没有其他行要从光标1
  15. 获取

    我认为你可以看到,那里有很多不必要的来回行动......事实上,这可能就是大部分时间花在你身上的地方。

    如果不是因为你的导师的要求(我假设它仅用于教学目的),你可以在一个更新声明中这样做,如下:

    update recibos
    set    f_p_camb_est = '20161212',
           op_cambest = 'SIMD0943'
    where  (imp_tot_rec - imp_cta) > 0
    and    f_p_camb_est = '29991231'; 
    

    (注意,如果f_p_camb_est是DATE数据类型,则需要使用DATEto_date()将字符串文字转换为DATE,例如DATE '29991231'to_date('20161212', 'yyyymmdd'),除非您'将传递一个已经是DATE数据类型的参数。)

    这样做意味着您可以将更新语句插入Pro * C应用程序,它将执行以下操作:

    1. 在Pro * C中,要求数据库执行更新语句
    2. 数据库执行更新语句
    3. 控制权返回Pro * C.
    4. 总共有2个上下文切换 - 比原始方法大幅减少!

      然而,你的导师已经要求(恕我直言)通过一次一个nis_rad更新行的愚蠢方式。

      在这种情况下,你仍然可以更好地完成数据库中的大部分工作 - 从数据库向你的应用程序发回行只有*没有*点,只是你的应用程序除了通过之外什么都不做他们回到数据库。在数据库端执行此操作的方式是在PL / SQL中,如下所示:

      begin
        for rec in (select distinct nis_rad, sec_nis
                    from   recibos
                    where  (imp_tot_rec - imp_cta) > 0
                    and    f_p_camb_est = '29991231')
        loop
          update recibos
          set    f_p_camb_est = '20161212',
                 op_cambest = 'SIMD0943'
          where  nis_rad = rec.nis_rad
          and    (imp_tot_rec - imp_cta) > 0
          and    f_p_camb_est = '29991231';
        end loop;
      end;
      /
      

      您可以直接从Pro * C将其作为匿名块调用,也可以将其作为数据库中的过程创建(可能带参数),然后从Pro * C调用该过程。

      无论哪种方式,总体流程如下:

      1. 在Pro * C中,要求数据库执行PL / SQL
      2. 数据库执行PL / SQL
      3. 控制权返回Pro * C.
      4. 我知道这看起来像2个上下文切换,但是当它在PL / SQL和SQL引擎之间切换时,你还需要考虑数据库中的上下文切换,它看起来像:

        1. PL / SQL引擎请求游标
        2. SQL引擎打开游标
        3. PL / SQL引擎从光标
        4. 请求一行
        5. SQL引擎传回一行
        6. PL / SQL引擎将信息存储在rec记录
        7. PL / SQL引擎要求SQL引擎根据rec记录中的nis_rad值执行update语句
        8. SQL引擎运行更新语句
        9. 重复步骤1 - 7,直到不再返回任何行
        10. 正如我确定你可以看到的那样,如果你刚刚运行单个更新语句,那么我们可以看到的不仅仅是2个上下文切换。

          希望能为你解决一些问题?