存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。
存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。
优点
存储过程可封装,并隐藏复杂的商业逻辑。
存储过程可以回传值,并可以接受参数。
存储过程无法使用 SELECT 指令来运行,因为它是子程序,与查看表,数据表或用户定义函数不同。
存储过程可以用在数据检验,强制实行商业逻辑等。
缺点
存储过程,往往定制化于特定的数据库上,因为支持的编程语言不同。当切换到其他厂商的数据库系统时,需要重写原有的存储过程。
存储过程的性能调校与撰写,受限于各种数据库系统。
一、存储过程的创建和调用
存储过程就是具有名字的一段代码,用来完成一个特定的功能。
创建的存储过程保存在数据库的数据字典中。
创建存储过程
CREATE [DBA] PROCEDURE 过程名(参数[, 参数 [ ,...]])
RETURNING 子句
语句块
END PROCEDURE
DOCUMENT 子句
WITH LISTING IN 文件名;
** 参数:**
[ {IN} | OUT | INOUT ] 参数名称 数据类型
默认是IN,可省略
RETURNING子句:
数据类型列表
语句块:
有效的SPL语句
DECUMENT子句:
对存储过程作的简单说明,可省略
WITH LISTING IN 文件名:
说明接受编译器输出信息的文件名,省略不输出
存储过程中的关键语法
声明存储过程:
CREATE PROCEDURE demo_in_parameter(p_in int)
存储过程语句块:
BEGIN .... END
变量定义与赋值:
-- 变量定义
define p1 int;
-- 赋值
let p1 = 0;
-- 或者
select tabid into p1 from systables where tabid = 1;
创建GBase 8s存储过程:
create procedure 存储过程名(参数)
存储函数:
create function 存储函数名(参数)
实例
创建数据库,数据表用于示例操作:
create table compny
(
id serial not null ,
branch varchar(100),
address varchar(100),
city varchar(40),
zipcode char(6),
phone varchar(18),
primary key (id)
);
insert into compny values (0,'总部','华苑产业区海泰发展六道6号海泰绿色产业基地J座','天津市西青区','300384','022-58815678');
insert into compny values (0,'北京分公司','太阳宫中路12号太阳宫大厦10层1008室','北京市朝阳区','100102','010-88866866');
insert into compny values (0,'南京分公司','奥体大街69号1栋20层','南京市建邺区','210019','025-87775809');
下面是存储过程的例子:
CREATE PROCEDURE read_address(p_branch varchar(100))
RETURNING varchar(100),varchar(100),char(6);
DEFINE v_branch,v_address varchar(100);
DEFINE v_zipcode char(6);
SELECT branch, address, zipcode INTO v_branch, v_address, v_zipcode
FROM compny
WHERE branch = p_branch;
RETURN v_branch, v_address, v_zipcode;
END PROCEDURE
DOCUMENT '读取分支机构信息,返回分支机构名称、地址及邮编'
WITH LISTING IN '/home/gbase/read_address_sql.lst';
解析:默认情况下,存储过程和默认数据库相关联,如果想指定存储过程创建在某个特定的数据库下,那么在过程名前面加数据库名做前缀。
调用存储过程:
call sp_name[(传参)];
> call read_address('北京分公司');
(expression) 北京分公司
(expression) 太阳宫中路12号太阳宫大厦10层1008室
(expression) 100102
解析:在存储过程中设置了需要传参的变量p_branch,调用存储过程的时候,通过传参将’北京分公司’赋值给p_branch,然后进行存储过程里的SQL操作。
存储过程体
存储过程体包含了在过程调用时必须执行的语句,例如:dml、ddl语句,if-then-else和while语句、声明变量语句等
过程体格式:以begin开始,以end结束(可嵌套)
BEGIN
BEGIN
BEGIN
statements;
END
END
END
注意:每个嵌套块及其中的每条语句,必须以分号结束,表示过程体结束的begin-end块(又叫做复合语句compound statement),则不需要分号。
二、存储过程的参数
存储过程的参数用在存储过程的定义,共有三种参数类型,IN,OUT,INOUT,形式如:
CREATE PROCEDURE 存储过程名([[{IN} |OUT |INOUT ] 参数名 数据类形…])
IN 输入参数:无关键字表示IN, 表示调用者向过程传入值(传入值可以是字面量或变量)
OUT 输出参数:表示过程向调用者传出值(可以返回多个值)(传出值只能是变量)
INOUT 输入输出参数:既表示调用者向过程传入值,又表示过程向调用者传出值(值只能是变量)
1、in 输入参数
create procedure in_param(p_in int) returning varchar(100);
define v_in int;
let v_in = p_in + 1;
return 'Value of p_in + 1 : ' || v_in;
end procedure;
调用存储过程
> call in_param(1);
(expression) Value of p_in + 1 : 2
1 row(s) retrieved.
2、out输出参数
--
create procedure out_param(out p_out int) returning varchar(100);
define v_in int;
let v_in = 1;
let p_out = v_in + 1;
return 'Value of v_in : ' || v_in;
end procedure;
-- 调用out_param
create procedure call_out_param() returning varchar(100);
define v_in int;
define v_rc varchar(100);
let v_in = -1;
call out_param(p_out = v_in) returning v_rc;
let v_rc = v_rc || ' , Value of p_out : ' || v_in;
return v_rc;
end procedure;
调用存储过程
> call call_out_param();
(expression) Value of v_in : 1 , Value of p_out : 2
1 row(s) retrieved.
call_out_param中, 作为p_out的参数的v_in变量需要指定的默认值(虽然无意义),值在存储过程out_param中改变。
3、inout输入参数
create procedure inout_param(inout p_inout int) returning varchar(100);
let p_inout = p_inout + 1;
return 'Value of p_inout : ' || p_inout;
end procedure;
-- 调用inout_param
create procedure call_inout_param() returning varchar(100);
define v_in int;
define v_rc varchar(100);
let v_in = 1;
call inout_param(p_inout = v_in) returning v_rc;
let v_rc = v_rc || ' , Value of p_inout : ' || v_in;
return v_rc;
end procedure;
调用存储过程
> call call_inout_param();
(expression) Value of p_inout : 2 , Value of p_inout : 2
1 row(s) retrieved.
call_inout_param中,作为p_inout参数的v_in变量需要指定值,值在存储过程inout_param中改变。
注意:
1、如果过程没有参数,也必须在过程名后面写上小括号例:
CREATE PROCEDURE sp_name ([proc_parameter[,...]]) ……
2、确保参数的名字不等于列的名字,否则在过程体中,参数名被当做列名来处理
三、变量
1. 变量定义
局部变量声明一定要放在存储过程体的开始:
DEFINE variable_name [,variable_name…] datatype ;
其中,datatype 为数据类型,如: int, float, date,varchar(length)
定义TEXT 或BYTE 类型变量时, 需在其变量前加关键字REFERENCES
可以使用LIKE定义与字段类型一致的数据类型
例如:
DEFINE v_x INT;
DEFINE v_name CHAR(15);
DEFINE v_this_day DATETIME YEAR TO DAY;
DEFINE v_tttt REFERENCES TEXT;
DEFINE v_bbb REFERENCES BYTE;
DEFINE v_customer LIKE orders.customer_num;
全局变量声明时需要指定默认值
例如:
DEFINE GLOBAL global_var INT DEFAULT 1;
2. 变量赋值
利用LET 语句 (LET 变量名 = 表达式值 [,variable_name = expression …])
利用SELECT … INTO 语句
利用CALL … RETURNING 语句
利用EXECUTE PROCEDURE … INTO 语句
例如:
LET a, b=c, d;
LET a, b=(SELECT empname, empno FROM emp WHERE empno=125);
SELECT empname INTO v_name FROM emp WHERE empno=120;
CALL read_address('北京分公司') RETURNING v_branch, v_address, v_zipcode;
EXECUTE PROCEDURE read_address('北京分公司') INTO v_branch, v_address, v_zipcode;
四、注释
存储过程可使用两种风格的注释
两个横杆–:该风格一般用于单行注释。
c 风格/* */: 一般用于多行注释。
例如:
create procedure in_param(p_in int) returning varchar(100);
-- 定义局部变量 v_in;
define v_in int;
/*
* 变量赋值,以及返回
*/
let v_in = p_in + 1;
return 'Value of p_in + 1 : ' || v_in;
end procedure;
五、存储过程的调用
用call和你过程名以及一个括号,括号里面根据需要,加入参数,参数包括输入参数、输出参数、输入输出参数。具体的调用方法可以参看上面的例子。
存储过程的查询
使用dbschema实用工具查询存储过程信息
语法:dbschema -d 数据库名 -f 过程名
dbschema -d demo -f in_param
输出结果:
DBSCHEMA Schema Utility GBASE-SQL Version 12.10.FC4G1AEE
create procedure "gbasedbt".in_param(p_in int) returning varchar(100);
-- 定义局部变量 v_in;
define v_in int;
/*
* 变量赋值,以及返回
*/
let v_in = p_in + 1;
return 'Value of p_in + 1 : ' || v_in;
end procedure;
或者使用SQL语句查询
select data
from sysprocedures, sysprocbody
where datakey = 'T'
and sysprocedures.procname = 'in_param' -- in_param 为存储过程名字
and sysprocedures.procid = sysprocbody.procid;
输出结果:
data create procedure in_param(p_in int) returning varchar(100);
-- 定义
局部变量 v_in;
define v_in int;
/*
* 变量赋值,以及返
回
*/
let v_in = p_in + 1;
return 'Value of p_in + 1 : ' || v_in;
end procedure;
1 row(s) retrieved.
存储过程的删除
删除一个存储过程比较简单,和删除表一样:
DROPP ROCEDURE [IF EXISTS] 存储过程名称
存储过程的控制语句
(1). 变量作用域
内部的变量在其作用域范围内享有更高的优先权,当执行到 end。变量时,内部变量消失,此时已经在其作用域外,变量不再可见了,应为在存储过程外再也不能找到这个申明的变量。
create procedure proc1() returning varchar(128);
define v_rc varchar(128);
let v_rc = "";
begin -- 层1
define v_1 int;
let v_1 = 1111;
begin -- 层2
define v_1 int;
let v_1 = 2222;
let v_rc = v_rc || " 层二 v_1的值是:" || v_1;
end
let v_rc = v_rc || " 层一 v_1的值是:" || v_1;
end
return v_rc;
end procedure;
调用存储过程
> call proc1();
(expression) 层二 v_1的值是:2222 层一 v_1的值是:1111
1 row(s) retrieved.
层一的变量v_1不因层二的变量v_1改变。
(2). 条件语句
1)、if … then … elif … then … else … end if 语句
create procedure proc2(p_in int) returning varchar(128);
define v_rc varchar(128);
let v_rc = "";
if p_in = 0 then
let v_rc = 'p_in的值是0';
elif p_in = 1 then -- 可以有多个elif ... then
let v_rc = 'p_in的值是1';
else
let v_rc = 'p_in的值是其它';
end if;
return v_rc;
end procedure;
2)、case … when … then … else … end case语句:
create procedure proc3(p_in int) returning varchar(128);
define v_rc varchar(128);
let v_rc = "";
case p_in
when 0 then
let v_rc = 'p_in的值是0';
when 1 then
let v_rc = 'p_in的值是1';
else
let v_rc = 'p_in的值是其它';
end case;
return v_rc;
end procedure;
(3). 循环语句
1)、 while ··· end while语句
create procedure proc4(p_in int) returning varchar(128);
define v_rc varchar(128);
let v_rc = "";
while p_in < 6
let v_rc = v_rc || '当前p_in的值为:' || p_in || '\n ';
let p_in = p_in + 1;
end while;
return v_rc;
end procedure;
2)、for … end for语句
create procedure proc5(p_in int) returning varchar(128);
define v_rc varchar(128);
define i int;
let v_rc = "";
for i = p_in to 5
let v_rc = v_rc || '当前i的值为:' || i || '\n ';
end for;
return v_rc;
end procedure;
3)、loop ··· end loop语句
loop 循环不需要初始条件,这点和 while 循环相似, exit when语句的意义是离开循环。
create procedure proc6(p_in int) returning varchar(128);
define v_rc varchar(128);
define i int;
let v_rc = "";
let i = p_in;
loop
let i = i + 1;
let v_rc = v_rc || '当前i的值为:' || i || '\n ';
exit when i = 5;
end loop;
return v_rc;
end procedure;
4)、for … loop … end loop语句
create procedure proc7(p_in int) returning varchar(128);
define v_rc varchar(128);
define i int;
let v_rc = "";
for i in (p_in to 6) loop
let v_rc = v_rc || '当前i的值为:' || i || '\n ';
exit when i = 5;
end loop;
return v_rc;
end procedure;
5)、while … loop … end loop语句
create procedure proc8(p_in int) returning varchar(128);
define v_rc varchar(128);
define i int;
let v_rc = "";
let i = p_in;
while(i<6) loop
let v_rc = v_rc || '当前i的值为:' || i || '\n ';
exit when i = 5;
let i = i + 1;
end loop;
return v_rc;
end procedure;