WHILE & FOR 循环

Posted

技术标签:

【中文标题】WHILE & FOR 循环【英文标题】:WHILE & FOR LOOP 【发布时间】:2019-12-18 00:17:56 【问题描述】:

我在以两种不同的循环方法(即 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;

以上这两个过程中哪个是循环的有效方式

【问题讨论】:

我怀疑有什么显着差异。 理想情况下,逻辑上没有区别,性能上也没有区别。 【参考方案1】:

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

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

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

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

【讨论】:

【参考方案2】:

以上这两个过程中哪个是循环的有效方式

在处理集合时,For Loop 有时会在集合为Sparse 时抛出错误。在这种情况下,使用WHILE LOOP 是有益的。两种循环机制在性能上是相同的。

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

在以下情况下使用数字 FOR loop 您的集合被密集填充(定义了最低和最高之间的每个索引值) 您想扫描整个集合,而不是在满足某些条件时终止扫描

相反,使用WHILE 循环 您的收藏可能很少 您可以在遍历集合中的所有元素之前终止循环

【讨论】:

感谢您的 cmets .. 1 更怀疑 PGA 区域受两个循环的影响如何...在 WHILE LOOP 中,我们必须明确启动索引值,但对于 FOR LOOP,它将自动处理。 .. PGA(程序全局区域)是一个内存区域,其中包含服务器进程的数据和控制信息。它是 Oracle 在服务器进程启动时创建的 non-shared 内存区域。 WHILE LOOP 和 FOR 循环采用 SGA sqlarea,如前所述,这取决于您如何使用来获取记录以进行集合。您必须使用 LIMIT 子句和 FOR LOOP 以避免高内存利用率。【参考方案3】:

正如@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;

是的,该行已在生产代码中运行多年,

【讨论】:

以上是关于WHILE & FOR 循环的主要内容,如果未能解决你的问题,请参考以下文章

while & for 循环的使用

WHILE & FOR 循环

4_while循环结构和break&continue

☀️ 学会编程入门必备 C# 最基础知识介绍——带你认识 C# 中的几种循环结构( for & while & do...while)

Python循环_for&while

While 和 For 循环 [关闭]