GBase 8s存储过程

  存储过程(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;

创建存储过程:

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;

标签: 存储过程, spl, GBase 8s SPL, procedure

添加新评论