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 循环的主要内容,如果未能解决你的问题,请参考以下文章
☀️ 学会编程入门必备 C# 最基础知识介绍——带你认识 C# 中的几种循环结构( for & while & do...while)