0

我在以两种不同的循环方法(即 WHILE 和 FOR LOOP)处理记录时遇到问题,请参考以下代码

 DECLARE
   TYPE rc_emp_type IS RECORD ( empno NUMBER,ename VARCHAR(30),sal   NUMBER, comm  NUMBER);   
  TYPE rc_emp_tab IS TABLE OF rc_emp_type;
  l_emp_rec rc_emp_tab := rc_emp_tab();
  TYPE rc_emp_calc_type IS RECORD ( empno NUMBER,
                                    ename VARCHAR(30),
                                    sal   NUMBER,
                                    comm  NUMBER,
                                    new_sal NUMBER);
     TYPE rc_emp_calc_tab IS TABLE OF rc_emp_calc_type;
  l_emp_calc_rec rc_emp_calc_tab := rc_emp_calc_tab();
  l_emp_fcalc_rec rc_emp_calc_tab := rc_emp_calc_tab();


  l_idx NUMBER;

  l_start_time TIMESTAMP;
  l_end_time TIMESTAMP;
  l_exe_time TIMESTAMP;
BEGIN
   SELECT empno,ename,sal,comm
   BULK COLLECT INTO l_emp_rec
   FROM emp;
   l_idx := l_emp_rec.FIRST;

   WHILE l_idx IS NOT NULL
   LOOP
     l_emp_calc_rec.EXTEND;
     l_emp_calc_rec(l_emp_calc_rec.LAST).empno := l_emp_rec(l_idx).empno;
     l_emp_calc_rec(l_emp_calc_rec.LAST).ename := l_emp_rec(l_idx).ename;
     l_emp_calc_rec(l_emp_calc_rec.LAST).sal := l_emp_rec(l_idx).sal;
     l_emp_calc_rec(l_emp_calc_rec.LAST).comm := l_emp_rec(l_idx).comm;
     l_emp_calc_rec(l_emp_calc_rec.LAST).new_sal := NVL(l_emp_rec(l_idx).sal,0) + NVL(l_emp_rec(l_idx).comm,0);

     l_idx := l_emp_rec.NEXT(l_idx);
   END LOOP;
      FOR l_idx  IN l_emp_rec.FIRST .. l_emp_rec.LAST
    LOOP
     l_emp_fcalc_rec.EXTEND;
     l_emp_fcalc_rec(l_emp_fcalc_rec.LAST).empno := l_emp_rec(l_idx).empno;
     l_emp_fcalc_rec(l_emp_fcalc_rec.LAST).ename := l_emp_rec(l_idx).ename;
     l_emp_fcalc_rec(l_emp_fcalc_rec.LAST).sal := l_emp_rec(l_idx).sal;
     l_emp_fcalc_rec(l_emp_fcalc_rec.LAST).comm := l_emp_rec(l_idx).comm;
     l_emp_fcalc_rec(l_emp_fcalc_rec.LAST).new_sal := NVL(l_emp_rec(l_idx).sal,0) + NVL(l_emp_rec(l_idx).comm,0);
   END LOOP;
  END;

在上述两个过程中,这是循环的有效方式

4

3 回答 3

1

正如@XING 指出的那样,区别不在于它们的效率,而在于稀疏集合会发生什么。您的示例没有遇到此问题,因为两者都是使用批量收集构建的,因此索引值没有间隙。但情况并非总是如此。下面的演示展示了它们之间的区别。

declare
    cursor c_numbers is
        select level+23 num   -- 23 has no particulat significence 
          from dual
         connect by level <= 5; -- nor does 5     
    type base_set is table of c_numbers%rowtype; 

    while_set base_set; 
    for_set   base_set;  

    while_index integer; -- need to define while loop index 

begin
  -- populate both while and for arrays. 
  open  c_numbers;
  fetch c_numbers bulk collect into while_set; 
  close c_numbers; 

  open  c_numbers;
  fetch c_numbers bulk collect into for_set; 
  close c_numbers;

  -- Make sparse 
  while_set.delete(3);
  for_set.delete(3);

  -- loop through with while
  while_index := while_set.first;
  while while_index is not null 
  loop
      begin
         dbms_output.put_line('While_Set(' || 
                              while_index  || 
                              ') = '       || 
                              while_set(while_index).num
                              );
         while_index := while_set.next(while_index);
      exception 
      when others then
         dbms_output.put_line('Error in While_Set(' || 
                              while_index           || 
                              ') Message='          ||
                              sqlerrm
                              );
      end;
  end loop;      

  -- loop through with for
  for for_index in for_set.first .. for_set.last 
  loop
      begin
         dbms_output.put_line('For_Set(' || 
                              for_index  || 
                              ') = '     || 
                              for_set(for_index).num
                              );
      exception 
      when others then
         dbms_output.put_line('Error in For_Set(' || 
                              for_index           || 
                              ') Message='        || 
                              sqlerrm
                              );
      end;
  end loop; 

end;

还可以尝试使用集合定义为的 for 循环:

type state_populations_t is table of number index by varchar2(20);
state_populations state_populations_t;

是的,这条线在生产代码中并且已经运行了多年,

于 2019-12-18T06:20:56.977 回答
1

在上述两个过程中,这是循环的有效方式

在处理 collectionFor Loop时,有时会在 collection 为Sparse. 在这种情况下,它有益于使用WHILE LOOP。两种循环机制在性能上是相同的。

稀疏:如果在最低和最高定义的索引值之间至少有一个未定义的索引值,则集合是稀疏的。例如,稀疏集合有一个元素分配给索引值 1,另一个分配给索引值 10,但两者之间没有任何内容。稀疏集合的反面是密集集合。

当您的集合被密集填充时使用数字FOR loop(定义了最低和最高之间的每个索引值)您想要扫描整个集合,如果满足某些条件则不终止扫描

相反,当您的集合可能稀疏时使用WHILE循环您可能会在遍历集合中的所有元素之前终止循环

于 2019-12-18T05:05:02.673 回答
1

如果您知道您的集合将被密集填充,例如由 BULK COLLECT 填充的集合,我建议您使用数字 FOR 循环。这假设密集填充,因此在这种情况下是最合适的。

每当您不能 100% 确定您的集合是密集填充时,您应该使用 WHILE 循环和 FIRST-NEXT 或 LAST-PRIOR 方法来遍历集合。

您可能会争辩说,您不妨一直使用 WHILE 循环。性能会很好,内存消耗没有什么不同....但是:您可能会以这种方式“隐藏”错误。如果集合应该是密集的,但事实并非如此,你永远不会知道。

最后,WHILE 循环可能比 FOR 循环表现更好:如果您的集合非常稀疏(例如,仅在索引值 -1M、0、1M、2M、3M 等中填充的元素) , FOR 循环会引发很多 NO_DATA_FOUND 异常。处理并继续处理所有这些异常将使循环执行非常缓慢。

于 2019-12-18T11:58:33.177 回答