欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

GBase 8s 存储过程

程序员文章站 2022-07-01 08:04:24
...

存储过程(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(IN p_in int)

存储过程语句块:

BEGIN .... END 

变量定义与赋值:

-- 变量定义
define p1 int;
-- 赋值
let p1 = 0;
-- 或者
select tabid into p1 from systables where tabid = 1;

创建mysql存储过程:

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;