10.2.7 有参存储过程

存储过程允许带有参数,参数的使用将增加存储过程的灵活性,给数据库编程带来极大的方便。本小节将介绍如何使用输入类型参数、输出类型参数和输入输出类型参数。

存储过程中如果使用了参数,在执行存储过程时必须为其指定参数。总的来说,参数可以是常量(也可以是经过初始化的变量)、变量、表达式等。而参数的赋值方式也比较灵活,下面的示例演示了在存储过程中如何传递参数。

过程有输入、输出、输入输出三种参数(这里不是指参数的数据类型)。其中,输入参数是默认的参数,也叫IN类型的参数。下面通过示例演示在存储过程中如何使用输入参数。

【示例7】使用输入参数的存储过程

该示例将根据输入的产品类型从表PRODUCTINFO中搜索符合要求的数据,并将其打印到屏幕。本示例中的参数为输入类型。具体脚本如下:


01 CREATE PROCEDURE PRODUCT_INTYPE_PRC(parm_ctgyname IN VARCHAR2)

02 AS

03 cur_ctgyid categroyinfo.categroyid%TYPE;—存放产品类型编码

04 cur_prtifo productinfo%ROWTYPE;—存放表productinfo的行记录

05

06 BEGIN

07 SELECT CATEGROYINFO.CATEGROYID INTO cur_ctgyid

08 FROM CATEGROYINFO

09 WHERE CATEGROYINFO.CATEGROYNAME=parm_ctgyname;

10 —根据类型编码得到产品类型名称

11

12 IF SQL%FOUND THEN

13 DBMS_OUTPUT.PUT_LINE('————————————————');

14 DBMS_OUTPUT.PUT_LINE(parm_ctgyname||':');

15 END IF;

16 FOR my_prdinfo_rec IN

17 (

18 SELECT*FROM PRODUCTINFO WHERE CATEGORY=cur_ctgyid

19 )

20 LOOP

21 DBMS_OUTPUT.PUT_LINE(

22 '产品名称:'||my_prdinfo_rec.PRODUCTNAME

23 ||'产品价格:'||my_prdinfo_rec.PRODUCTPRICE

24 ||'产品数量:'||my_prdinfo_rec.QUANTITY

25 );

26 END LOOP;

27

28 EXCEPTION

29 WHEN NO_DATA_FOUND THEN

30 DBMS_OUTPUT.PUT_LINE('没有数据!');

31 WHEN TOO_MANY_ROWS THEN

32 DBMS_OUTPUT.PUT_LINE('数据过多!');

33 END;

34 /


【代码解析】

❑第1~2行创建存储过程。存储过程包括IN类型的参数,表示该参数为输入类型。此时可以省略关键字IN。

❑第3~4行表示声明过程内部变量。其中,cur_ctgyid表示产品类型编码,而cur_prtifo表示行类型的记录。

❑第7~9行表示用传进的参数parm_ctgyname作为查询条件从表CATEGROYINFO中找出对应的编码类型,并把该类型编码放到变量cur_ctgyid中。

❑第12~15行表示利用隐式游标的属性进行判断:如果有记录则把该类型名称输出到屏幕;如果没有记录或根据条件查询的记录过多,则会进入异常块部分。

❑第16~26行表示根据产品类型编码从表PRODUCTINFO中查询数据,并把得到的数据输出到屏幕。

❑第28~32行表示异常块部分。当出现NO_DATA_FOUND或TOO_MANY_ROWS异常时会中断操作进入异常部分。

【执行效果】

在SQL*Plus中创建该存储过程,并执行,得到的效果如图10.6所示。

10.2.7 有参存储过程 - 图1

图 10.6 示例7执行效果

从图10.6可以看到,当执行存储过程时输入了参数“洗衣机”,下面列出了3条相关数据。这是一个简单的示例,相信大家能很快掌握。

这里需要注意的是,如果存储过程有使用参数,那么调用该过程时一定要为其指定参数,否则会出现错误提示。不过可以采取一些措施避免此类错误的发生,例如使用默认值。接下来的示例将展示存储中的参数如何使用默认值。

【示例8】使用参数的默认值

本示例提供了根据输入的产品类型查询对应产品类型编码的功能。与上个示例不同的是,该示例中的输入参数使用了默认值功能。具体实现见如下脚本。

1)创建函数。函数将返回“雨具”字符串。


01 CREATE OR REPLACE FUNCTION DEFT RETURN VARCHAR2

02 IS

03 BEGIN

04 DBMS_Output.PUT_LINE('——已进入到函数——');

05 DBMS_Output.PUT_LINE('默认类型是雨具');

06 RETURN'雨具';

07 END DEFT;

08 /


【代码解析】

❑第1~2行表示创建一个函数,函数名为DEFT,返回值是VARCHAR2类型。

❑第4~5行表示简单输出。当调用时会输出这些提示语句。

❑第6行表示返回的值,这里返回“雨具”。也就是说,该函数返回值是“雨具”。

在SQL*Plus中执行该函数,并检验是否成功。函数的创建以及执行过程如图10.7所示。

10.2.7 有参存储过程 - 图2

图 10.7 函数DEFT的创建执行过程

从图10.7中可以看出,该函数已经创建完成并成功返回设定值。除此之外,在SQL*Plus中继续执行如下创建过程脚本。

2)创建存储过程。该过程输入产品类型,在表CATEGROYINFO中查询符合该产品类型的编码并输出到屏幕。


01 CREATE PROCEDURE PRODUCT_INTYPE_DEFT_PRC(parm_ctgyname

02 IN VARCHAR2 DEFAULT DEFT())

03 AS

04 cur_ctgyid categroyinfo.categroyid%TYPE;

05

06 BEGIN

07 SELECT CATEGROYINFO.CATEGROYID INTO cur_ctgyid

08 FROM CATEGROYINFO

09 WHERE CATEGROYINFO.CATEGROYNAME=parm_ctgyname;

10

11 IF SQL%FOUND THEN

12 DBMS_OUTPUT.PUT_LINE('————————————————');

13 DBMS_OUTPUT.PUT_LINE(parm_ctgyname||'对应的编码是:'||cur_ctgyid);

14 END IF;

15

16 EXCEPTION

17 WHEN NO_DATA_FOUND THEN

18 DBMS_OUTPUT.PUT_LINE('没有数据!');

19 WHEN TOO_MANY_ROWS THEN

20 DBMS_OUTPUT.PUT_LINE('数据过多!');

21 END;

22 /


【代码解析】

❑第1~2行表示创建存储过程,该过程是带有IN类型的参数,并且有定义输入类型参数的默认值。该默认值为DEFT()函数。

❑第7~9行根据输入的参数值(产品类型)查询对应的产品类型编码,并放入变量cur_ctgyid中。

❑第11~14行表示如果有符合要求的记录则输出到屏幕。

❑第16~20行表示异常块,其中定义了两个异常,这里不再解释,读者可以参考前面的章节。

本示例为输入参数指定了默认值。也就是说,调用该存储过程时,如果没有为其指定参数,则该过程使用自定义的默认值,这里会执行DEFT函数,利用它的返回值作为参数值。

【执行效果】

在SQL*Plus中执行该过程,效果如图10.8所示。

10.2.7 有参存储过程 - 图3

图 10.8 示例8执行效果

从图10.8中可看到一共执行了两次该存储过程。第一次没有指定参数,但从效果中可以看出该过程没有提示错误,而是直接执行了DEFT函数的内容,并根据该函数返回的产品类型查询出了对应的产品类型编码。而第二次执行该过程指定了参数为“电视”,这次执行过程中并没有执行函数DEFT。由此可以看出,默认值只能在没有输入参数值的时候起作用。

提示 该过程使用了函数作为参数的默认值。其实,这里完全可以写一个确切的字符串作为默认值,如“雨具”。

存储过程中OUT类型的参数也被称为输出类型的参数。这样的存储过程会有相关返回值给调用它的程序,在程序中几乎可以把它当做一个变量使用,利用它给调用者中的变量初始化。下面的示例演示了如何使用该类型的参数。

【示例9】输出类型参数的使用

完成该示例需要两个步骤,第一部分就是被调用的存储过程,它里面包含OUT类型参数;第二部分就是调用者,这里也使用存储过程。详细实现过程如下:

1)创建被调用的存储过程。该过程提供根据输入的产品类型查询出对应的产品类型编码功能,并将得到的编码放到输出参数中。相关脚本如下:


01 CREATE PROCEDURE PRODUCT_OUTTYPE_PRC(parm_ctgyname IN VARCHAR2,

02 parm_ctgyid OUT VARCHAR2)

03 AS

04 BEGIN

05 SELECT CATEGROYINFO.CATEGROYID INTO parm_ctgyid

06 FROM CATEGROYINFO

07 WHERE CATEGROYINFO.CATEGROYNAME=parm_ctgyname;

08 IF SQL%FOUND THEN

09 DBMS_OUTPUT.PUT_LINE('传出参数是:'||parm_ctgyid);

10 END IF;

11

12 EXCEPTION

13 WHEN NO_DATA_FOUND THEN

14 DBMS_OUTPUT.PUT_LINE('没有数据!');

15 WHEN TOO_MANY_ROWS THEN

16 DBMS_OUTPUT.PUT_LINE('数据过多!');

17 END PRODUCT_OUTTYPE_PRC;

18 /


【代码解析】

❑第1~3行表示创建存储过程,该过程有两个参数,其中第一个parm_ctgyname为输入参数,表示输入的是产品类型名称,而parm_ctgyid是输出参数,表示的是产品类型编码。

❑第5~7行表示根据产品名称查询产品编码。

❑第12~16行表示异常部分。

2)创建调用存储过程。该过程根据输入的产品类型以及价格从表PRODUCTINFO中查询符合要求的数据并输出到屏幕。详细脚本如下:


01 CREATE PROCEDURE PRODUCT_CLOUTTYPE_PRC(parm_ctgyname IN VARCHAR2,

02 parm_pric NUMBER)

03 AS

04 cur_ctgyid categroyinfo.categroyid%TYPE;—存放产品类型编码

05 cur_prtifo productinfo%ROWTYPE;—存放表productinfo的行记录

06

07 BEGIN

08 PRODUCT_OUTTYPE_PRC(parm_ctgyname,cur_ctgyid);

09 IF SQL%FOUND THEN

10 DBMS_OUTPUT.PUT_LINE('————————————————-');

11 DBMS_OUTPUT.PUT_LINE(parm_ctgyname||'对应的编码是:'||cur_ctgyid);

12 END IF;

13 FOR my_prdinfo_rec IN

14 (

15 SELECT*FROM PRODUCTINFO

16 WHERE CATEGORY=cur_ctgyid

17 AND PRODUCTINFO.PRODUCTPRICE<parm_pric

18 )

19 LOOP

20 DBMS_OUTPUT.PUT_LINE(

21 '产品名称:'||my_prdinfo_rec.PRODUCTNAME

22 ||'产品价格:'||my_prdinfo_rec.PRODUCTPRICE

23 ||'产品数量:'||my_prdinfo_rec.QUANTITY

24 );

25 END LOOP;

26 END;

27 /


【代码解析】

❑第1~3行是创建过程,该过程有两个参数,分别为parm_ctgyname和parm_ctgyid,这两个参数都是输入类型的参数。

❑第4~5行表示声明两个变量。

❑第8行表示调用过程PRODUCT_OUTTYPE_PRC,该过程需要两个参数。其中,cur_ctgyid占用了输出参数的位置。也就是说,该过程的返回值将初始化给变量cur_ctgyid。

❑第9~12行判断并输出产品类型名称对应编码。

❑第13~25行表示循环输出符合记录的数据。其中,第15~17行表示查询指定产品类型以及低于指定价格的数据。

【执行效果】

在SQL*Plus中成功创建以上两个存储过程,并执行如下脚本调用PRODUCT_CLOUTTYPE_PRC过程。查询价格在3000以下的洗衣机相关记录。脚本如下:


EXEC PRODUCT_CLOUTTYPE_PRC('洗衣机',3000);


执行效果见图10.9。

10.2.7 有参存储过程 - 图4

图 10.9 示例9执行效果

使用OUT类型参数时需要注意以下两点:

1)OUT类型的参数需要用变量填充,而不能用常量或表达式填充。

2)如果过程中有被调用的过程因发生未处理的异常而退出,那么调用者通常得不到任何OUT参数的值(包括发生异常之前已经得到的OUT类型的参数值),而在发生的异常被处理的情况下退出,那么之前得到的OUT的参数值将会被调用者获得。

存储过程还有一种参数类型,是输入输出类型,也叫IN OUT类型参数。此类型的参数同OUT类型参数性质相似,不过它既支持输入也支持输出。以下就是该类型参数的示例。

【示例10】使用输入输出类型的参数

下面演示如何使用输入输出类型参数,完成该示例同样需要两个步骤。详细实现过程如下:

1)创建被调用的存储过程。该过程提供根据输入的产品类型编码和降价比例对表PRODUCTINFO的数据进行降价修改,并返回修改的记录数。脚本如下:


01 CREATE PROCEDURE PRODUCT_INOUTTYPE_PRC(parm_ctgyid IN VARCHAR2,

02 parmparm_pric IN OUT NUMBER)

03 AS

04 BEGIN

05 UPDATE PRODUCTINFO

06 SET PRODUCTPRICE=PRODUCTINFO.PRODUCTPRICE*(1-parmparm_pric)

07 WHERE PRODUCTINFO.CATEGORY=parm_ctgyid;

08 IF SQL%FOUND THEN

09 parmparm_pric:=SQL%ROWCOUNT;

10 END IF;

11 END PRODUCT_INOUTTYPE_PRC;


【代码解析】

❑第1~3行表示创建存储过程,其中包含两个参数:parm_ctgyid表示产品类型编码是输入参数,parmparm_pric用来输入时表示降价比例,而用来输出时则表示修改语句修改的记录数。

❑第5~7行表示根据提供的产品类型编码和降价比例修改PRODUCTINFO的价格记录。

❑第9行表示利用游标的属性得到修改的记录数。该记录数实际赋值给了该存储过程的输入输出参数parmparm_pric。

2)创建调用存储过程。该过程输入产品类型名称,根据产品类型名称查询出产品类型编码并调用PRODUCT_INOUTTYPE_PRC存储过程。最终输出修改的记录数。脚本如下:


01 CREATE PROCEDURE PRODUCT_CLINOUTTYPE_PRC(parm_ctgyname IN VARCHAR2)

02

03 AS

04 cur_ctgyid categroyinfo.categroyid%TYPE;—存放产品类型编码

05 cur_pric number;

06

07 BEGIN

08 SELECT CATEGROYINFO.CATEGROYID INTO cur_ctgyid

09 FROM CATEGROYINFO

10 WHERE CATEGROYINFO.CATEGROYNAME=parm_ctgyname;

11

12 PRODUCT_INOUTTYPE_PRC(cur_ctgyid,cur_pric);

13 IF cur_pric>0 THEN

14 DBMS_OUTPUT.PUT_LINE('共修改'||cur_pric||'条记录。');

15 END IF;

16

17 EXCEPTION

18 WHEN NO_DATA_FOUND THEN

19 DBMS_OUTPUT.PUT_LINE('没有数据!');

20 WHEN TOO_MANY_ROWS THEN

21 DBMS_OUTPUT.PUT_LINE('数据过多!');

22 END;

23 /


【代码解析】

❑第1~3行创建存储过程,参数表示产品类型名称。

❑第4~5行声明变量。cur_ctgyid用于存放产品类型编码,cur_pric用于存储在存储过程PRODUCT_INOUTTYPE_PRC返回的记录数。

❑第8~10行表示根据产品类型查询产品编码。

❑第12行表示调用存储过程PRODUCT_INOUTTYPE_PRC,此时利用返回值初始化cur_pric变量。

❑第13~15行表示利用条件语句判断后输出修改的记录数。

❑第17~21行表示异常块。

该示例同示例9的操作方式差别不大,接下来查看执行效果。

【执行效果】

在SQL*Plus中创建以上两个存储过程,确认没有错误提示后执行该过程。效果如图10.10所示。

10.2.7 有参存储过程 - 图5

图 10.10 示例10执行效果

给存储过程的参数赋值时,除了以上接触到的按照存储过程的参数顺序赋值之外,还有另外一种赋值的方式。这种方式可以看做显式地为某个变量赋值,它指定了赋值的参数对象,因此这种方式不用考虑原存储过程中参数的顺序。例如,在该示例中PRODUCT_CLINOUTTYPE_PRC过程的第12行,利用如下的赋值方式可以达到同样的效果:


PRODUCT_INOUTTYPE_PRC(parmparm_pric=>cur_pric,parm_ctgyid=>cur_ctgyid);


这段脚本表示的是参数parmparm_pric的值是变量cur_pric,而参数parm_ctgyid的值是变量cur_ctgyid。这样赋值的结果和以前没有差异,读者可以根据实际需求自行把握。