WHILE 和 FOR 循环
WHILE & FOR LOOP
我在用两种不同的循环方法处理记录时遇到问题,即 WHILE 和 FOR 循环,请参考以下代码
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;
上面这两个程序中哪个是循环的有效方式
Out of these two above procedure which is the efficient way of looping
在处理 collection 时,For Loop
有时会在 collection 为 Sparse
时抛出错误。在这种情况下,使用 WHILE LOOP
是有益的。两种循环机制在性能上是相同的。
稀疏:- 如果在定义的最低和最高索引值之间至少有一个未定义的索引值,则 collection 是稀疏的。例如,稀疏 collection 有一个元素分配给索引值 1,另一个元素分配给索引值 10,但两者之间没有任何元素。稀疏的反义词collection是密集的。
使用数字 FOR loop
您的 collection 被密集填充(定义了最低和最高之间的每个索引值)
您想要扫描整个 collection,而不是在满足某些条件时终止扫描
相反,使用 WHILE
循环
您的 collection 可能稀疏
您可以在遍历 collection
中的所有元素之前终止循环
正如@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;
是的,该行在生产代码中并且已经 运行 多年,
如果您知道您的 collection 将是 densely-filled,就像 BULK COLLECT 填充的 collection 的情况一样,我建议您使用数字 FOR 循环。假设 densely-filled 因此在这种情况下最合适。
当您不能 100% 确定 collection 是 densely-filled 时,您应该使用 WHILE 循环和 FIRST-NEXT 或 LAST-PRIOR 方法来遍历collection.
您可能会争辩说,您还不如一直使用 WHILE 循环。性能会很好,内存消耗没有什么不同....但是:你可能 "hide" 这样会出错。如果 collection 是 应该 是密集的,但事实并非如此,你永远不会知道。
最后,有一种方法可以使 WHILE 循环比 FOR 循环表现更好:如果您的 collection 非常稀疏(例如,仅在索引值 -1M、0、 1M、2M、3M 等),FOR 循环将引发大量 NO_DATA_FOUND 异常。处理并继续所有这些异常将使循环执行非常缓慢。
我在用两种不同的循环方法处理记录时遇到问题,即 WHILE 和 FOR 循环,请参考以下代码
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;
上面这两个程序中哪个是循环的有效方式
Out of these two above procedure which is the efficient way of looping
在处理 collection 时,For Loop
有时会在 collection 为 Sparse
时抛出错误。在这种情况下,使用 WHILE LOOP
是有益的。两种循环机制在性能上是相同的。
稀疏:- 如果在定义的最低和最高索引值之间至少有一个未定义的索引值,则 collection 是稀疏的。例如,稀疏 collection 有一个元素分配给索引值 1,另一个元素分配给索引值 10,但两者之间没有任何元素。稀疏的反义词collection是密集的。
使用数字 FOR loop
您的 collection 被密集填充(定义了最低和最高之间的每个索引值)
您想要扫描整个 collection,而不是在满足某些条件时终止扫描
相反,使用 WHILE
循环
您的 collection 可能稀疏
您可以在遍历 collection
正如@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;
是的,该行在生产代码中并且已经 运行 多年,
如果您知道您的 collection 将是 densely-filled,就像 BULK COLLECT 填充的 collection 的情况一样,我建议您使用数字 FOR 循环。假设 densely-filled 因此在这种情况下最合适。
当您不能 100% 确定 collection 是 densely-filled 时,您应该使用 WHILE 循环和 FIRST-NEXT 或 LAST-PRIOR 方法来遍历collection.
您可能会争辩说,您还不如一直使用 WHILE 循环。性能会很好,内存消耗没有什么不同....但是:你可能 "hide" 这样会出错。如果 collection 是 应该 是密集的,但事实并非如此,你永远不会知道。
最后,有一种方法可以使 WHILE 循环比 FOR 循环表现更好:如果您的 collection 非常稀疏(例如,仅在索引值 -1M、0、 1M、2M、3M 等),FOR 循环将引发大量 NO_DATA_FOUND 异常。处理并继续所有这些异常将使循环执行非常缓慢。