📄 第四章 pl-sql的控制结构 - pl-sql用户指南与参考 - whatiswhat.htm
字号:
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>FOR</STRONG> i <STRONG>IN</STRONG> <STRONG>REVERSE</STRONG> 1 .. 3 <STRONG>LOOP</STRONG> <EM>-- assign the values 3,2,1 to i</EM><BR> sequence_of_statements <EM>-- executes three times</EM><BR><STRONG>END</STRONG> <STRONG>LOOP</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>FOR循环里,循环计数器只能当作常量来引用且不能为它赋值,如下例: </P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>FOR</STRONG> ctr <STRONG>IN</STRONG> 1 .. 10 <STRONG>LOOP</STRONG><BR> <STRONG>IF</STRONG> <STRONG>NOT</STRONG> finished <STRONG>THEN</STRONG><BR> <STRONG>INSERT</STRONG> <STRONG>INTO</STRONG> ...<BR> <STRONG>VALUES</STRONG> (ctr); <EM>-- legal</EM><BR><BR> factor := ctr * 2; <EM>-- legal</EM><BR> <STRONG>ELSE</STRONG><BR> ctr := 10; <EM>-- not allowed</EM><BR> <STRONG>END</STRONG> <STRONG>IF</STRONG>;<BR><STRONG>END</STRONG> <STRONG>LOOP</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<UL>
<LI>迭代法 </LI></UL>
<P>循环范围的边界可以是文字、变量或表达式,但它们都必须是数字。否则PL/SQL会抛出预定义异常VALUE_ERROR。如下例,下界不一定非得是1。但循环计数器只能是每次增加1。
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap>j <STRONG>IN</STRONG> -5 .. 5<BR>k <STRONG>IN</STRONG> <STRONG>REVERSE</STRONG> first..last<BR>step <STRONG>IN</STRONG> 0..TRUNC(high/low) * 2
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>在PL/SQL内部,它会把边界值赋给一个临时的PLS_INTEGER变量,并在需要的时候把值转换成最接近的整数。PLS_INTEGER的范围是-2**31到2**31之间。所以,如果边界值超过这个范围,我们就会得到一个数字溢出错误:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>DECLARE</STRONG><BR> hi <STRONG>NUMBER</STRONG> := 2 ** 32;<BR><STRONG>BEGIN</STRONG><BR> <STRONG>FOR</STRONG> j <STRONG>IN</STRONG> 1 .. hi <STRONG>LOOP</STRONG> <EM>-- causes a 鈥檔umeric overflow鈥?error</EM><BR> ...<BR> <STRONG>END</STRONG> <STRONG>LOOP</STRONG>;<BR><STRONG>END</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>有些语言提供了STEP子句,它能让我们指定循环增量。PL/SQL没有这样的结构,但我们可以在FOR循环内扩大循环计数器的倍数来实现这样的功能。在下面的例子中,我们今天的日期赋给索引表的第一个、第五个和第十五个元素。
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>DECLARE</STRONG><BR> <STRONG>TYPE</STRONG> datelist <STRONG>IS</STRONG> <STRONG>TABLE</STRONG> <STRONG>OF</STRONG> <STRONG>DATE</STRONG><BR> <STRONG>INDEX</STRONG> <STRONG>BY</STRONG> <STRONG>BINARY_INTEGER</STRONG>;<BR><BR> dates datelist;<BR> k <STRONG>CONSTANT</STRONG> <STRONG>INTEGER</STRONG> := 5; <EM>-- set new increment</EM><BR><STRONG>BEGIN</STRONG><BR> <STRONG>FOR</STRONG> j <STRONG>IN</STRONG> 1 .. 3 <STRONG>LOOP</STRONG><BR> dates(j * k) := <STRONG>SYSDATE</STRONG>; <EM>-- multiply loop counter by increment</EM><BR> <STRONG>END</STRONG> <STRONG>LOOP</STRONG>;<BR> ...<BR><STRONG>END</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<UL>
<LI>动态范围 </LI></UL>
<P>PL/SQL允许我们在运行时决定循环的范围,如下例所示: </P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>SELECT</STRONG> COUNT(empno)<BR> <STRONG>INTO</STRONG> emp_count<BR> <STRONG>FROM</STRONG> emp;<BR><BR><STRONG>FOR</STRONG> i <STRONG>IN</STRONG> 1 .. emp_count <STRONG>LOOP</STRONG><BR> ...<BR><STRONG>END</STRONG> <STRONG>LOOP</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>emp_count在运行时是未知的;SELECT语句在运行时才返回结果值。 </P>
<P>如果循环范围中的下界值超过上界值会怎样呢?如下例所示,循环内的语句序列就不会被执行,控制权直接交给下一个语句:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><EM>-- limit becomes 1</EM><BR><STRONG>FOR</STRONG> i <STRONG>IN</STRONG> 2 .. LIMIT <STRONG>LOOP</STRONG><BR> sequence_of_statements <EM>-- executes zero times</EM><BR><STRONG>END</STRONG> <STRONG>LOOP</STRONG>;<BR><EM>-- control passes here</EM>
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<UL>
<LI>作用域规则 </LI></UL>
<P>循环计数器只在循环内部定义,我们不能在循环外引用它。循环退出后,循环计数器就会失效,如下例: </P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>FOR</STRONG> ctr <STRONG>IN</STRONG> 1 .. 10 <STRONG>LOOP</STRONG><BR> ...<BR><STRONG>END</STRONG> <STRONG>LOOP</STRONG>;<BR>sum := ctr - 1; <EM>-- not allowed</EM>
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>我们不需要显式声明循环计数器,因为它会被隐式地声明为INTEGER类型本地变量。下面的例子中本地声明覆盖了全局声明:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>DECLARE</STRONG><BR> ctr <STRONG>INTEGER</STRONG>;<BR><STRONG>BEGIN</STRONG><BR> ...<BR> <STRONG>FOR</STRONG> ctr <STRONG>IN</STRONG> 1 .. 25 <STRONG>LOOP</STRONG><BR> ...<BR> <STRONG>IF</STRONG> ctr > 10 <STRONG>THEN</STRONG> ... <EM>-- refers to loop counter</EM><BR> <STRONG>END</STRONG> <STRONG>LOOP</STRONG>;<BR><STRONG>END</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>如果想在上例中引用全局变量,我们就得借助标签和点标志,例如: </P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><<main>><BR><STRONG>DECLARE</STRONG><BR> ctr <STRONG>INTEGER</STRONG>;<BR> ...<BR><STRONG>BEGIN</STRONG><BR> ...<BR> <STRONG>FOR</STRONG> ctr <STRONG>IN</STRONG> 1 .. 25 <STRONG>LOOP</STRONG><BR> ...<BR> <STRONG>IF</STRONG> main.ctr > 10 <STRONG>THEN</STRONG> <EM>-- refers to global variable</EM><BR> ...<BR> <STRONG>END</STRONG> <STRONG>IF</STRONG>;<BR> <STRONG>END</STRONG> <STRONG>LOOP</STRONG>;<BR><STRONG>END</STRONG> main;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>同样的作用域规则也适用于嵌套FOR循环。如下面的例子,两个循环计数器的名字相同,所以,用从内层循环引用外层循环的循环计数器,就必须使用标签和点标志:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><<outer>><BR><STRONG>FOR</STRONG> step <STRONG>IN</STRONG> 1 .. 25 <STRONG>LOOP</STRONG><BR> <STRONG>FOR</STRONG> step <STRONG>IN</STRONG> 1 .. 10 <STRONG>LOOP</STRONG><BR> ...<BR> <STRONG>IF</STRONG> outer.step > 15 <STRONG>THEN</STRONG> ...<BR> <STRONG>END</STRONG> <STRONG>LOOP</STRONG>;<BR><STRONG>END</STRONG> <STRONG>LOOP</STRONG> outer;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<UL>
<LI>使用EXIT语句 </LI></UL>
<P>EXIT语句可以立即结束一个FOR循环。例如,下面的循环语句正常情况应该执行十次,但是,如果FETCH语句取得数据失败,循环就会立即终止,无论它执行过多少次:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>FOR</STRONG> j <STRONG>IN</STRONG> 1 .. 10 <STRONG>LOOP</STRONG><BR> <STRONG>FETCH</STRONG> c1 <STRONG>INTO</STRONG> emp_rec;<BR> <STRONG>EXIT</STRONG> <STRONG>WHEN</STRONG> c1%NOTFOUND;<BR> ...<BR><STRONG>END</STRONG> <STRONG>LOOP</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>在使用EXIT时,我们可以结束任何封闭循环,而不仅仅是当前循环。只要在我们想结束的封闭循环上加上标签,然后在EXIT语句中引用它,就能结束做过标记的FOR循环了,如下例:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><<outer>><BR><STRONG>FOR</STRONG> i <STRONG>IN</STRONG> 1 .. 5 <STRONG>LOOP</STRONG><BR> ...<BR> <STRONG>FOR</STRONG> j <STRONG>IN</STRONG> 1 .. 10 <STRONG>LOOP</STRONG><BR> <STRONG>FETCH</STRONG> c1 <STRONG>INTO</STRONG> emp_rec;<BR> <STRONG>EXIT</STRONG> outer <STRONG>WHEN</STRONG> c1%NOTFOUND; <EM>-- exit both FOR loops</EM><BR> ...<BR> <STRONG>END</STRONG> <STRONG>LOOP</STRONG>;<BR><STRONG>END</STRONG> <STRONG>LOOP</STRONG> outer;<BR><EM>-- control passes here</EM>
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P class=title1>四、顺序控制:GOTO和NULL语句</P>
<P>与IF和LOOP语句不通,GOTO和NULL语句对PL/SQL编程来说不是必须的。PL/SQL结构中很少用到GOTO语句。有时,它就是用于简化逻辑的。NULL用于改善可读性使条件语句看起来更加清晰。
</P>
<P>滥用GOTO能使结构混乱,不易理解和维护(有时被称为意大利面条式代码 - spaghetti
code)。所以,GOTO语句的使用一定要有节制。例如,要从一个深嵌套中跳到异常控制块,要用异常抛出而不是使用GOTO语句。
</P>
<P class=title2>1、GOTO语句</P>
<P>GOTO语句可以无条件跳到一个标签处。标签名称在它所处的作用范围内必须是唯一的。执行的时候,GOTO语句会把控制权交给一个做了标记的语句或块。GOTO语句可以向上或向下跳转,示例如下:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>BEGIN</STRONG><BR> ...<BR> <STRONG>GOTO</STRONG> insert_row;<BR> ...<BR> <<insert_row>><BR> <STRONG>INSERT</STRONG> <STRONG>INTO</STRONG> emp <STRONG>VALUES</STRONG> ...<BR><STRONG>END</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>再看一个向上跳转的例子:</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>BEGIN</STRONG><BR> ...<BR> <<update_row>><BR> <STRONG>BEGIN</STRONG><BR> <STRONG>UPDATE</STRONG> emp <STRONG>SET</S
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -