📄 第八章 pl-sql子程序 - pl-sql用户指南与参考 - whatiswhat.htm
字号:
noWrap><STRONG>DECLARE</STRONG><BR> ...<BR> <STRONG>PROCEDURE</STRONG> award_bonus <STRONG>IS</STRONG><BR> <STRONG>BEGIN</STRONG><BR> calc_rating(...); <EM>-- undeclared identifier</EM><BR> ...<BR> <STRONG>END</STRONG>;<BR> <STRONG>PROCEDURE</STRONG> calc_rating (...) <STRONG>IS</STRONG><BR> <STRONG>BEGIN</STRONG><BR> ...<BR> <STRONG>END</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>这种情况下,我们只要简单地把过程calc_rating放到award_bonus之前就可以了。但是简单的做法不一定总是有效的,如:两个过程相互引用或是我们就想按逻辑或字母顺序来定义他们。</P>
<P>我们可以使用"向前声明"来解决这个问题,向前声明由子程序说明和一个分号组成。在下面的例子中,向前声明通知PL/SQL,过程calc_rating的体部分可以在块的后面找到。
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>DECLARE</STRONG><BR> <STRONG>PROCEDURE</STRONG> calc_rating ( ... ); <EM>-- forward declaration</EM><BR> ...
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>虽然形式参数列表在向前声明中已经出现过了,但它也必须出现在子程序体中。子程序体可以放在向前声明之后的任何地方,但它们必须出现在同一个程序单元中。
</P>
<P class=title1>六、子程序打包</P>
<P>我们可以把逻辑相关的子程序打包后放到数据库中。那样,子程序就能被许多应用程序共享。子程序说明部分放在包说明部分;子程序体放在包体,子程序体对应用程序是不可见的。因此,包能帮助我们隐藏程序的实现细节。如下例:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>CREATE</STRONG> <STRONG>PACKAGE</STRONG> emp_actions <STRONG>AS</STRONG> <EM>-- package spec</EM><BR> <STRONG>PROCEDURE</STRONG> hire_employee(emp_id <STRONG>INTEGER</STRONG>, NAME <STRONG>VARCHAR2</STRONG>, ...);<BR><BR> <STRONG>PROCEDURE</STRONG> fire_employee(emp_id <STRONG>INTEGER</STRONG>);<BR><BR> <STRONG>PROCEDURE</STRONG> raise_salary(emp_id <STRONG>INTEGER</STRONG>, amount <STRONG>REAL</STRONG>);<BR> ...<BR><STRONG>END</STRONG> emp_actions;<BR><BR><STRONG>CREATE</STRONG> <STRONG>PACKAGE</STRONG> <STRONG>BODY</STRONG> emp_actions <STRONG>AS</STRONG> <EM>-- package body</EM><BR> <STRONG>PROCEDURE</STRONG> hire_employee(emp_id <STRONG>INTEGER</STRONG>, NAME <STRONG>VARCHAR2</STRONG>, ...) <STRONG>IS</STRONG><BR> <STRONG>BEGIN</STRONG><BR> ...<BR> <STRONG>INSERT</STRONG> <STRONG>INTO</STRONG> emp<BR> <STRONG>VALUES</STRONG> (emp_id, NAME, ...);<BR> <STRONG>END</STRONG> hire_employee;<BR><BR> <STRONG>PROCEDURE</STRONG> fire_employee(emp_id <STRONG>INTEGER</STRONG>) <STRONG>IS</STRONG><BR> <STRONG>BEGIN</STRONG><BR> <STRONG>DELETE</STRONG> <STRONG>FROM</STRONG> emp<BR> <STRONG>WHERE</STRONG> empno = emp_id;<BR> <STRONG>END</STRONG> fire_employee;<BR><BR> <STRONG>PROCEDURE</STRONG> raise_salary(emp_id <STRONG>INTEGER</STRONG>, amount <STRONG>REAL</STRONG>) <STRONG>IS</STRONG><BR> <STRONG>BEGIN</STRONG><BR> <STRONG>UPDATE</STRONG> emp<BR> <STRONG>SET</STRONG> sal = sal + amount<BR> <STRONG>WHERE</STRONG> empno = emp_id;<BR> <STRONG>END</STRONG> raise_salary;<BR> ...<BR><STRONG>END</STRONG> emp_actions;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>我们还能够直接在包体内定义子程序而不用在包说明部分编写它们的说明。但是,这样的子程序只能在包内的使用。
</P>
<P class=title1>七、形参VS实参</P>
<P>子程序使用参数来传递信息。调用时子程序参数列表中引用的变量或表达式是实际参数(actual
parameter,以下简称实参)。例如,下面的过程列出了两个实参emp_num和amout: </P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD noWrap>raise_salary(emp_num, amount);
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>下面的过程调用演示了用表达式作为实参: </P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap>raise_salary(emp_num, merit + cola);
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>子程序声明和子程序体中引用的变量是形式参数(formal
parameter,以下简称形参)。例如,下面的过程声明了两个形参emp_id和amount: </P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>PROCEDURE</STRONG> raise_salary(emp_id <STRONG>INTEGER</STRONG>, amount <STRONG>REAL</STRONG>) <STRONG>IS</STRONG><BR><STRONG>BEGIN</STRONG><BR> <STRONG>UPDATE</STRONG> emp<BR> <STRONG>SET</STRONG> sal = sal + amount<BR> <STRONG>WHERE</STRONG> empno = emp_id;<BR><STRONG>END</STRONG> raise_salary;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>好的编程习惯就是使用不同命名的形参和实参。</P>
<P>调用过程raise_salary时,实参的内容会被赋值到对应的形参上,如果有必要的话,在赋值之前PL/SQL会帮助我们进行类型转换。例如,下面对raise_salary的调用就是有效的:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap>raise_salary(emp_num, <EM>'2500'</EM>);
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>实参和它对应的形参必须类型兼容,例如,PL/SQL是不能把数据从DATE类型转到REAL类型的。下面的过程调用就会引起预定义异常VALUE_ERROR,因为PL/SQL不能把第二个实参转成一个数字:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap>raise_salary(emp_num, <EM>'$2500'</EM>); <EM>-- note the dollar sign</EM>
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P class=title1>八、位置标示法VS名字标示法</P>
<P>在调用子程序时,我们既可以使用位置标示法又可以使用名字标示法来编写实参。也就是说,我们可以按位置或名称来把实参和形参关联起来。如下例所示:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>DECLARE</STRONG><BR> acct <STRONG>INTEGER</STRONG>;<BR> amt <STRONG>REAL</STRONG>;<BR><BR> <STRONG>PROCEDURE</STRONG> credit_acct(acct_no <STRONG>INTEGER</STRONG>, amount <STRONG>REAL</STRONG>) <STRONG>IS</STRONG> ...
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>我们可以使用四种等价的方法来调用过程credit_acct:</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>BEGIN</STRONG><BR> credit_acct(acct, amt); <EM>-- positional notation</EM><BR> credit_acct(amount => amt, acct_no => acct); <EM>-- named notation</EM><BR> credit_acct(acct_no => acct, amount => amt); <EM>-- named notation</EM><BR> credit_acct(acct, amount => amt); <EM>-- mixed notation</EM>
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P class=title2>1、使用位置标示法</P>
<P>第一个过程调用使用了位置标示法。PL/SQL编译器将第一个实参acct和第一个形参acct_no关联,并把第二个实参amt和第二个形参amount关联。
</P>
<P class=title2>2、使用名字标示法</P>
<P>第二个过程调用使用了名字标示法。箭头(=>)作为关联操作符,把左边的实参和右边的形参关联起来。
</P>
<P>第三个过程调用也使用了名字标示法,而且我们可以随意安排参数的位置。所以,我们不需要知道形参的在参数列表中的顺序。
</P>
<P class=title2>3、使用混合标示法</P>
<P>第四个过程调用使用了名字标示法和位置标示法。在这种情况下,位置标示法必须在名字标示法之前,不能反过来使用,像下面这样的调用方法就是不合法的:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap>credit_acct(acct_no => acct, amt); <EM>-- illegal</EM>
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P class=title1>九、指定子程序参数模式</P>
<P>我们可以使用参数的模式来定义形式参数的行为。一共有三种模式:IN、OUT和IN
OUT。但是,最好避免在函数中使用OUT和IN
OUT模式。函数的作用是用来接受零个或多个参数然后返回一个值。用函数返回多个值不是个好习惯。同样,函数应该避免产生负影响,那样会改变那些对子程序来说是非本地的变量值。
</P>
<P class=title2>1、使用IN模式</P>
<P>IN模式能让我们把值传递给被调用子程序,在子程序中,IN模式参数的作用就像常量一样。因此,它不能被赋值。例如,下面的赋值语句就会引起编译错误:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>PROCEDURE</STRONG> debit_account(acct_id <STRONG>IN</STRONG> <STRONG>INTEGER</STRONG>, amount <STRONG>IN</STRONG> <STRONG>REAL</STRONG>) <STRONG>IS</STRONG><BR> minimum_purchase <STRONG>CONSTANT</STRONG> <STRONG>REAL</STRONG> <STRONG>DEFAULT</STRONG> 10.0;<BR> service_charge <STRONG>CONSTANT</STRONG> <STRONG>REAL</STRONG> <STRONG>DEFAULT</STRONG> 0.50;<BR><STRONG>BEGIN</STRONG><BR> <STRONG>IF</STRONG> amount < minimum_purchase <STRONG>THEN</STRONG><BR> amount := amount + service_charge; <EM>-- causes compilation error</EM><BR> <STRONG>END</STRONG> <STRONG>IF</STRONG>;<BR> ...<BR><STRONG>END</STRONG> debit_account;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>IN模式形参对应的实参可以是常量、文字、被初始化的变量或是表达式。与OUT和IN
OUT模式的参数不同,我们可以为IN模式的参数初始化一个默认值。 </P>
<P class=title2>2、使用OUT模式</P>
<P>OUT模式的参数能让我们把值返回给子程序的调用者。在子程序中,OUT模式参数的作用就像变量。这也就意味着我们可以把它当作本地变量来使用,例如:
</P>
<BLOCKQUOTE>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -