1、简介
存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象,任何一个设计良好的数据库应用程序都应该用到存储过程。
存储过程是由流控制和SQL语句书写的过程,这个过程经编译和优化后存储在数据库服务器中,应用程序使用时只要调用即可。
2、重要概念和疑问解答
1)何谓informix中的SPL?
SPL(Stored Procedure Language)是informix中提供的一种提供流程控制(分支和循环)的SQL。它包括SPL过程(没有返回值)和SPL函数(有返回值)。SPL将在创建的时候被解析和优化,它以可执行的方式保存在system catalog表中。
2)使用SPL为什么会改善性能?
i)因为它运行在informix引擎中,所以减少了I/O;
ii)降低了应用的复杂性;
iii)对频率较高的重复操作使用更好。
3)SPL的灵活性表现在哪些部分?
i)它可在其它SQL语法中被调用;
ii)它也可以在触发器中使用;
iii)可在SQL中添加流程控制;
iv)很容易维护。
4)SPL的参数
i)可以传递很多个参数;
ii)SPL的参数的最大限制是32K;
iii)除了如下两种外可以是任何一种SQL数据类型:
Serial/Serial8
Text/Byte (能够使用REFERENCES关键字传递)。
iv)能够使用很复杂或者用户自定义的数据类型;
v)可以通过default为参数指定默认值。
5)SPL函数的返回值
i)可以使用RETURNING或RETURNS关键字定义返回值的返回类型;
ii)除了如下两种数据类型外可以为任何数据类型:
Serial/Serial8
Text/Byte (可以使用REFERENCES关键字传递)。
iii)在过程体内必须至少有一个return语句;
iv)可以为返回值取一个名称。例如:
RETURNING INTEGER AS comp_res;
6)SPL的重载
可定义多个具有相同名字的SPL函数;例如如下SPL函数虽然名称相同,但是参数类型或各不相同:
CREATE FUNCTION val_comp (val1 CHAR ( 25 ), val2 CHAR ( 25 ))
CREATE FUNCTION val_comp (val1 DECIMAL , val2 DECIMAL )
…
可使用SPECIFIC关键字指定SPL函数的简短的、独一无二的被重载的SPL的函数的名称。这个名字在数据库中是唯一的。例如:
CREATE FUNCTION val_comp (val1 CHAR ( 25 ), val2 CHAR ( 25 )) SPECIFIC string_comp
CREATE FUNCTION val_comp (val1 DECIMAL , val2 DECIMAL ) SPECIFIC dec_comp
7)何谓语句块?
i)语句块是一组SPL或SQL语句;
ii)隐式的语句块包含在CREATE PROCEDURE/FUNCTION和END PROCEDURE/FUNCITON之间;
iii)可使用BEGIN和END来显式的知名内嵌在另一个语句块中的语句块,例如:
DEFINE x INT ;
LET x = 15 ;
INSERT INTO table1 VALUES (x, ' amigo ' );
BEGIN -- 显式的语句块的开始
DEFINE y INT ;
LET y = 16 ;
INSERT INTO table1 VALUES ( y, ' xingxing ' );
END -- 显式的语句块的结束
END PROCEDURE ; -- 隐式的语句块的结束
8)如何定义变量?
i)是语句块中使用DEFINE定义变量;
ii)变量在内存中,而不是在数据库中保存;
iii)有本地变量和全局变量两种;
iv)变量可以是除了如下两种类型之外的任何一种SQL数据类型和扩展类型:
Serial/Serial8
Text/Byte (可以使用REFERENCES关键字来声明)。
定义变量举例如下:
DEFINE p person_type; -- 用户自定义的数据类型person_type
DEFINE mymusic REFERENCES BYTE; -- 使用REFERENCES关键字定义BYTE类型
9)本地变量和全局变量比较
本地变量:
i)本地变量值在SPL中有效;
ii)在SPL函数或过程结束后就被复位;
iii)它不能有默认值;
iv)它的作用域是在它定义的语句块中,或者任何内嵌语句块中;
v)本地变量能够被重新定义在另一个语句块中。
下面来看一个本地变量的实例:
DEFINE x,y,z INT ;
LET x = 5 ;
LET y = 10 ;
LET z = x + y; -- z等于15
BEGIN
DEFINE x, q INT ; -- x被重新定义
DEFINE z CHAR ( 5 ); -- z被重新定义
LET x = 100 ;
LET q = x + y; -- q=110
LET z = 'amigo ' ; -- 给z设置了一个新的值
END
LET y = x; -- y等于5
LET x = z; -- z的值是15,而不是amigo
END PROCEDURE;
全局变量:
i)在相同数据库中使用相同的用户会话的地方都能得到全局变量的值;
ii)必须有一个默认值;
iii)必须在每一个要使用它的SPL函数或过程中定义;
iv)不能是一个集合变量。
例如如下定义了两个SPL函数,名称分别为func1和func2,func1参考语句如下:
DEFINE GLOBAL gvar INT DEFAULT 2 ;
LET gvar = gvar + 1 ;
RETURN gvar;
END FUNCTION ;
func2参考语句如下:
DEFINE GLOBAL gvar INT DEFAULT 5 ;
LET gvar = gvar + 1 ;
RETURN gvar;
END FUNCTION ;
如果执行两者的顺序如下:
EXECUTE FUNCTION func2();
则执行完第一句后,gvar被设置了默认值2,且执行了加1的操作,所以第一句执行完毕后gvar的值为3.
接着执行第二句,第这次不在设置gvar的默认值,因此在3的基础上再执行了加1操作,执行完毕后gvar的值为4.
若执行两者的顺序如下:
EXECUTE FUNCTION func1();
则执行完第一句后,gvar被设置了默认值5,且执行了加1的操作,所以第一句执行完毕后gvar的值为6.
接着执行第二句,第这次不在设置gvar的默认值,因此在6的基础上再执行了加1操作,执行完毕后gvar的值为7.
10)给变量赋值
i)使用一个未定义的变量将会报错;
ii)给已定义的变量赋值的方法:
* 使用LET语句直接给变量赋初值,参考语句如下:
*使用SELET INTO语句将查询到的结果给变量赋值,参考语句如下:
*使用CALL...RETURNING语句将返回结果赋给变量,参考语句如下:
*使用EXECUTE FUNCTION INTO语句,将返回结果赋给变量,参考语句如下:
3、语法
1)创建SPL过程
… {语句块}
END PROCEDURE ;
例如,在下面的实例中创建了一个名为set_status的存储过程,可传入myid和mystatus两个参数,在这个存储过程中,对item_inventory表进行update操作,id字段满足myid的记录将status字段更新为mystatus。参考语句如下:
UPDATE item_inventory SET status = mystatus WHERE id = myid;
END PROCEDURE ;
2)创建SPL函数
RETURNING list SPECIFIC specific_name
… {语句块}
END FUNCTION ;
例如,创建一个名为val_comp的SPL函数,可传入val1和val2两个参数,在该函数中,比较这两个变量,如果两者相等,返回0,否则返回1。参考语句如下:
RETURNING INTEGER ;
DEFINE res INTEGER ;
IF (val1 = val2) THEN
LET res = 0 ;
ELSE
LET res = 1 ;
END IF ;
RETURN res;
END FUNCTION ;
4、SPL中的流程控制
1)分支语句if...then...else...end if
参考实例:
statements
ELIF ( condition ) THEN
statements
…
ELSE
statements
END IF ;
参考实例:
WHEN < value1 > THEN
statements
WHEN < value2 > THEN
statements
…
ELSE
statements
END CASE ;
参考语法如下:
FOR count = 2 TO 30 STEP 2
FOR count IN ( 2 , 5 , 7 , 9 )
FOR count IN ( 2 to 8 STEP 2 , 12 to 19 , 22 )
FOR name IN ("AMY", " MAX ",
( SELECT name FROM customer WHERE customer_num = 100 ) )
参考语法如下所示:
执行的语句
END WHILE ;
WHILE (status matches "A * ")
WHILE ( EXISTS ( SELECT name FROM customer WHERE cus_num = 100 ))
WHILE (status IN ("A", "I", "D"))
i)被用来取多行数据;
ii)它打开了一个游标;
iii)游标用来取得当前的行,以便进行行的更新或删除操作。
参考实例如下:
FROM customer
WHERE location = “UK"
sum_sal += ind_sal;
END FOREACH;
RETRUN sum_sal;
都可以被使用在for、foreach和while循环语句中。
参考实例如下:
LET j = i;
WHILE (j > 0 )
LET id = foo(j);
IF (id = 5 ) THEN
LET j = j – 1 ;
CONTINUE WHILE ; -- 不执行后续的操作,继续回到while循环接着执行
END IF ;
LET sum = sum + 5 ;
LET j = j – 1 ;
IF ( sum > 500 ) THEN
EXIT FOR ; -- 退出for循环
END IF ;
END WHILE ;
END FOR ;
RETURN sum ;
1)ON EXCEPTION语句
ON EXCEPTION语句语句提供了异常的捕获和处理机制。在IN中指定要捕获的错误,并指定当异常发生时需要执行的动作。在一个语句块中允许有多个ON EXCEPTION语句。
ON EXCEPTION语句必须在DEFINE语句之后,并在在任何可执行的语句块之前。并且它在内嵌的语句块中也是有效的。它使用SET语句来接收SQL、ISAM错误码和错误信息。
参考实例如下所示:
DEFINE sql_err INTEGER ;
DEFINE isam_err INTEGER ;
DEFINE err_txt CHAR ( 200 );
ON EXCEPTION IN ( - 206 ) SET sql_err, isam_err, err_txt
CREATE TABLE tab1 ( col1 INT , col2 INT );
INSERT INTO tab1 VALUES ( 1 , 2 );
INSERT INTO tab1 VALUES ( 2 , 3 );
END EXCEPTION
INSERT INTO tab1 VALUES ( 1 , 2 ); -- 如果tab1不存在时,跳到异常处理
INSERT INTO tab1 VALUES ( 2 , 3 );
END PROCEDURE ;
该语句在存储过程发生错误时恢复语句。
如下实例展示了WITH RESUME的使用,当异常处理完毕后,继续后续的语句处理。参考语句如下:
ON EXCEPTION IN ( - 206 )
CREATE TABLE tab1 (col1 INT , col2 INT );
INSERT INTO tab1 VALUES ( 1 , 2 );
END EXCEPTION WITH RESUME;
INSERT INTO tab1 VALUES ( 1 , 2 ); -- 如果tab1不存在,跳到异常处理
INSERT INTO tab1 VALUES ( 2 , 3 ); -- 异常处理完成后,继续该句进行处理
INSERT INTO tab1 VALUES ( 3 , 4 );
END PROCEDURE ;
i) 该语句用来创建错误;
ii)它能够用来指定SQL错误、ISAM错误和错误信息;
iii)用该语句创建的错误,能够被ON EXCEPTION捕获;
iv)可以使用指定的错误码-746来表示自定义的错误消息。
如下实例使用ON EXCEPTION语句指明了当发生错误时将错误码、错误信息插入到自定义的错误表my_error_table,当传入的参数小于1时,抛出自定义的-746异常,其余情况成功将数据插入到tab1表中,参考语句如下:
DEFINE sql_err INTEGER ;
DEFINE isam_err INTEGER ;
DEFINE err_txt CHAR ( 200 );
ON EXCEPTION IN ( - 746 ) SET sql_err, isam_err, err_txt
INSERT INTO my_error_table values (sql_err, isam_err, err_txt);
END EXCEPTION;
IF (a < 1 ) THEN
RAISE EXCEPTION - 746 , 0 , "插入值必须大于0";
END IF ;
INSERT INTO tab1 VALUES ( 1 , a);
END PROCEDURE ;
可使用EXECUTE PROCEDURE语句来执行SPL存储过程,使用EXECUTE FUNCTION来执行SPL函数;
参考实例1:
DEFINE myvalue INT ;
CALL foo() RETURNING myvalue;
INSERT INTO table1 VALUES (myvalue);
RETURN myvalue;
END FUNCTION ;
1)使用DROP PROCEDURE来删除SPL存储过程;
2)使用DROP FUNCTION来删除SPL函数;
3)使用DROP ROUTINE来删除SPL存储过程或SPL函数。
几个参考实例如下:
DROP PROCEDURE foo ( INTEGER );
DROP SPECIFIC PROCEDURE foo_int;
DROP FUNCTION foo_ret;
DROP FUNCTION foo_ret ( INTEGER );
DROP SPECIFIC FUNCTION foo_ret_int;
DROP ROUTINE foo;
DROP ROUTINE foo_ret;