1. DB2存储过程执行原理
通常情况下,在客户-服务器体系结构中,运行在客户端的应用程序通过SQL对服务器端的数据库进行访问时,每一条SQL语句是分别地、一句一句地从客户端向服务器发出请求,然后数据库服务器再将结果一个一个地返回给应用程序。但是,有一些应用程序,涉及的服务器端数据库处理活动很多,而与用户的交互活动较少,在这种情况下,将有关数据库的处理活动以储存过程的形式放在数据库服务器上完成,则可以大大减轻网上传输流量,提高应用程序性能。
2. DB2中编写应用程序的方法
可使用几种不同的程序设计接口来存取 DB2 数据库。您可以:
将静态和动态 SQL 语句嵌入应用程序。
在应用程序中编写“DB2 调用层接口”(DB2 CLI) 的函数调用,以调用动态 SQL 语句。
开发调用“Java 数据库链接”应用程序设计接口 (JDBC API) 的 Java 应用程序和小程序。
开发符合“数据存取对象 (DAO) ”和“远程数据对象 (RDO) ” 规范的 Microsoft Visual Basic 和 Visual C++ 应用程序,以及使用“对象链接和嵌入数据库 (OLE DB) 桥接”的“ActiveX 数据对象”(ADO) 应用程序。
要执行备份和复原数据库等管理功能,应用程序可以使用 DB2 API。
应用程序存取 DB2 数据库的方式将取决于想要开发的应用程序类型。例如,如果想开发数据输入应用程序,可以选择将静态 SQL 语句嵌入应用程序。如果想开发在WEB上执行查询的应用程序,可能要选择 Net.Data、Perl 或 Java等等。
3. 静态SQL编程
3.1 程序结构
/*声明包含的头文件*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sqlenv.h>
#include <sql.h>
/*声明一个SQLCA实例
EXEC SQL INCLUDE SQLCA;
void main()
{/*声明宿主变量*/
EXEC SQL BIGIN DECLARE SECTION
。
。
EXEC SQL END DECLARE SECTION;
/*连接到数据库*/
。
/*SQL操作*/
。
。
。
/*错误处理*/
EXEC SQL WHENEVER NOTFOUND GO TO …
EXEC SQL WHENEVER SQLERROR GO TO …
EXEC SQL WHENEVER SQLWARNING GO TO CONTINUE
//断开数据库连接(connect reset)
}
3.2 C语言与SQL语言数据类型对照
图www.5sozy.com
对于decimal数据类型,在c中没有与之对应的数据类型,可以将其转换成字符串类型或浮点类型。
3.3 如何生成可执行的应用程序
3.4.1预编译
创建源文件之后,必须对每一个含有SQL语句的宿主语言文件用PREP命令进行预编译。预编译器将源文件中的SQL语句注释掉,对那些语句生成DB2运行时API调用。
在预编译一个应用之前,必须连接到一个数据库服务器,不论是自动连接还是显性连接。即使你在客户端工作站上预编译应用程序、预编译器在客户端产生的修改后源文件和信息,预编译器也需要使用服务器连接来执行一些确认任务。
预编译器也创建数据库管理器在处理针对某个数据库的SQL语句时需要的信息。这些信息存储在一个程序包或者一个捆绑文件或者两者之中,视预编译器的选项而定。
下面是使用预编译器的一个典型例子。预编译一个名叫filename.sqc的C嵌入式SQL源文件,发出下面的命令创建一个C源文件,默认名字为filename.c,和一个捆绑文件,默认名字为filename.bnd:
DB2 PREP filename.sqc BINDFILE
预编译器最多产生四种类型的输出:
修改后的源文件
程序包
捆绑文件
信息文件
3.4.2捆绑
捆绑(bind)是创建数据库管理器在应用执行时为了访问数据库而需要的程序包的过程。捆绑可以在预编译时指定PACKAGE选项隐含地完成,或者使用BIND命令依据预编译过程中产生的捆绑文件显性地完成。
下面是使用BIND命令的一个典型例子。将名为filename.bnd的捆绑文件捆绑到数据库,使用下面的命令:
DB2 BIND filename.bnd
每一个独立预编译的源代码模块都需要创建一个程序包。如果一个应用有5个源文件,其中3个需要预编译,那么要创建3个程序包或者3个捆绑文件。默认上,每一个程序包的名字与产生.bnd文件的源文件名字相同,但是只要前8个字符。如果新建的程序包名字与已存在于数据库中的程序包名相同,新的程序包将替换原先存在的程序包。要显性地指定一个不同的程序包名,必须在PREP命令使用PACKAGE USING选项。
3.4 游标的应用
3.4.1游标的声明
在使用游标前需要进行定义,它是关于一个查询的名字,查询可以使任何有效的SELECT语句。如下面格式声明游标:
EXEC SQL DECLARE C1 CURSOR FOR
SELECT PNAME,DEPT FROM STAFF
WHERE JOB=:host_var;
声明一个游标的语法如下:
3.4.2FETCH语句
FETCH语句读取结果行中游标所指当前行的下一行的数据,并将对应的字段值赋给语句中的宿主变量。执行FETCH语句时,游标必须打开。FETCH语句语法如下:
FETCH cursor_name INTO 宿主变量
4. 存储过程原理
4.1 执行原理
一个完整的使用储存过程的应用程序由两部分组成:一是储存过程本身,它被存放在
数据库服务器端并数据库服务器上运行;二是对存储过程进行调用的客户端应用程序,它在客户端上运行。客户端应用程序与服务器端储存过程分别运行在两个不同的进程空间中,并且有不同的功能划分。客户端应用程序客户端CAE存储过程DB2数据库
客户端应用程序的主要功能是:
为有关数据结构和主变量定义、分配并初始化存储空间;
与数据库进行连接;
通过SQL CALL语句调用存储过程
完成事务的提交(COMMIT)和回滚(ROLLBACK)(注:除非采用分布式工作单元,服务器端的存储过程也可以完成事务的提交和回滚);
执行CONNECT RESET语句;
服务器端储存过程的主要功能是:
接收客户端应用程序传送的SQLCA和SQLDA等数据库结构;
作为与客户端应用程序相同的事务在数据库服务器上运行;
向客户端应用程序返回SQLCA和其它输出数据;
4.2 存储过程的编写
存储过程的编写与其他的DB2应用程序的编写方法一样,只是存储过程具有固定的过程头,必须如下声明一个存储过程:
SQL_API_RC_API_FN proc_name(
Void *reserved1,
Void *reserved2,
Void sqlda *sqlda,
Void sqlca *sqlca
);
在过程体内的实现与其他的应用程序编写方式相同,下面是一个简单的例子,他完成了对staff表中符合条件的一些员工记录作修改的功能。例子中所涉及的表存在于DB2的样例数据库(Sample)中(由于篇幅的关系这里只介绍部分,所包含的完整样例见附件test.sqc)。
在这个例子中,先声明了一个vJob的宿主变量由来存放外部程序传来的参数值。他的数据类型为字符串型,长度是10。
接着,将外部传入的参数值传递给这个宿主变量。
/*声明宿主变量开始*/
EXEC SQL BEGIN DECLARE SECTION;
char vJob[10];
EXEC SQL END DECLARE SECTION;
/*声明宿主变量结束*/
/*获取传递的参数,将外部参数传给宿主变量*/
strncpy(vJob,(char *)inout_sqlda->sqlvar[0].sqldata,inout_sqlda->sqlvar[0].sqllen);
vJob[inout_sqlda->sqlvar[0].sqllen]='\0';
当接收到参数值后,执行更新的操作。在存储过程中执行sql语句的时候需要在sql语句前面加上EXEC SQL的关键字
/*执行更新的操作*/
EXEC SQL update staff set years=10 WHERE job=:vJob;
4.3 如何编译存储过程
其实在DB2所带的样本程序目录中就包含了这些编译程序的批处理文件这里将这些批处理文件加以整合就成了一个完整的编译存储过程的批处理文件,当然相应的编译程序还是需要的,比如MS VC++的编译器,或者C的编译器等等。该批处理文件见附件makesp.bat。
该批处理文件使用起来比较简单,首先切换到Db2的命令行环境,调用格式如下
makesp [prog_name] [db_name] [username] [password]
输入的四个参数分别为存储过程源文件名称[prog_name],捆绑的数据库别名[db_name],以及连接数据库的用户名和密码[username] [password],注意该用户需要有创见应用程序的权限。
这个批处理文件的执行过程:首先根据参数连接到需要捆绑的数据库上,其次对源文件进行预编译Prep和捆绑Bind;此过程将会生成.c文件和.bnd文件。接下来用C/C++编译器对生成的.c源文件进行编译和连接Link,执行成功后会生成一个.dll文件以及库文件.lib和一个.exp文件。这个dll文件就是编译成功后的存储过程执行文件,最后将这个dll文件存放在$DB2PATH\function目录下。在整个的编译过程中会有信息提示如下:
/*行消息 test.sqc
------ --------------------------------------------------------------------
SQL0060W "C" 预编译程序正在执行。
SQL0091W 预编译或绑定已结束,有 "0" 个错误和 "0" 个警告。
行 消息 test.bnd
------ --------------------------------------------------------------------
SQL0061W 绑定程序正在执行。
SQL0091N 绑定结束,具有 "0" 个错误和 "0" 个警告。
test.c
Microsoft (R) Incremental Linker Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
Creating library test.lib and object test.exp
这样,一个存储过程的编译过程就完成了。
当然你也可以先测试一下存储过程是否正确的被执行,在安装DB2时选择安装DB2开发客户机后会增加一个Stored Procedure Builder的工具,可以用它来进行测试(版本7为止还不支持对已有存储过程的分布调试。据说版本8支持,但笔者尚未那到这个版本还不清楚是否支持)。首先选择连接到数据库上,然后选择要测试的存储过程点击“运行”选项,如下图
在结果信息中可以看到,存储过程输出值
ADMINISTRATOR.TEST - 输出参数的值:
VCODE = 0
同时我们可以比较执行前后的数据变化情况:
执行存储过程前的数据情况
select * from staff where job='Clerk'
------------------------------------------------------------------------
ID NAME DEPT JOB YEARS SALARY COMM
------ --------- ------ ----- ------ --------- ---------
80 James 20 Clerk - 13504.60 128.20
110 Ngan 15 Clerk 5 12508.20 206.60
120 Naughton 38 Clerk - 12954.75 180.00
130 Yamaguchi 42 Clerk 6 10505.90 75.60
170 Kermisch 15 Clerk 4 12258.50 110.10
180 Abrahams 38 Clerk 3 12009.75 236.50
190 Sneider 20 Clerk 8 14252.75 126.50
200 Scoutten 42 Clerk - 11508.60 84.20
230 Lundquist 51 Clerk 3 13369.80 189.65
250 Wheeler 51 Clerk 6 14460.00 513.30
330 Burke 66 Clerk 1 10988.00 55.50
350 Gafney 84 Clerk 5 13030.50 188.00
select * from staff where job='Clerk'
------------------------------------------------------------------------
IID NAME DEPT JOB YEARS SALARY COMM
------ --------- ------ ----- ------ --------- ---------
80 James 20 Clerk 10 13504.60 128.20
110 Ngan 15 Clerk 10 12508.20 206.60
120 Naughton 38 Clerk 10 12954.75 180.00
130 Yamaguchi 42 Clerk 10 10505.90 75.60
170 Kermisch 15 Clerk 10 12258.50 110.10
180 Abrahams 38 Clerk 10 12009.75 236.50
190 Sneider 20 Clerk 10 14252.75 126.50
200 Scoutten 42 Clerk 10 11508.60 84.20
230 Lundquist 51 Clerk 10 13369.80 189.65
250 Wheeler 51 Clerk 10 14460.00 513.30
330 Burke 66 Clerk 10 10988.00 55.50
350 Gafney 84 Clerk 10 13030.50 188.00
执行存储过程前的数据情况
4.4 如何注册一个存储过程
create procedure test(in vJob char(10),out vCODE integer)
external name 'test!test'
language c parameter style db2dari;
CREATE PROCEDURE语句的作用是向数据库服务器注册一个新的存储过程;
test为存储过程的指定名。客户端应用程序可以在CALL语句使用这一名字调用相应的存储过程;
LANGUAGE C 在CREATE PROCEDURE语句中是必不可少的,其作用是指明存储过程体的语言接口约定。该子句的另一个选项是LANGUAGE JAVA;
EXTERNAL NAME(外部名)表示用户为实现所定义的存储过程而编写的程序代码段的名称;
PARAMETER STYLE 的作用是说明向存储过程传递参数以及从存储过程接收返回结果的有关约定。DB2DARI表明存储过程使用的参数传递约定将与C语言的函数调用和连接约定相兼容
4.5 客户端调用存储过程实例(以Borland Delphi5.0为例)
几乎所有支持数据库的RAD快速应用程序开发工具中都提供了对于存储过程的支持,Delphi也不例外。它用一个名为StoredProc的控件来支持存储过程的应用开发,下面将以它为基础讲述客户端调用存储过程的实例。
在工程中加入控件StoredProc,设置它的DatabaseName和StoredProcName属性后
你就可以使用下面的代码来完成调用存储过程了。
var
code:integer;
begin
StoredProc_Test.Close;
StoredProc_Test.params[0].AsString := 'Clerk';
StoredProc_Test.Prepare;
StoredProc_Test.ExecProc;
code:= StoredProc_Test.ParamByName('vcode').AsInteger;
case code of
0: showmessage('执行成功');
else abort;
end;
StoredProc_Test.UnPrepare;
end;
【附录】
SQLDA结构
Struct
{
char sqldaid[8];//存放常量:’SQLDA’
long sqlabc;//sqlda的大小
short sqln;// sqlvar项数
short sqld;//当前使用的sqlvar项数
struct sqlvar sqlvar[1];//第一个sqlvar项
};
struct sqlvar
{
short sqltype;//变量的数据类型
short sqllen;//变量的数据长度
char *sqldata;//指向变量数据值的指针
short *sqlind;//指向空指示符的指针
struct sqlname sqlname;//变量名称
};
struct sqlname
{
short length;名称长度(1..30)
char data[30];变量名或列名
};
SQLCA结构
struct sqlca
{unsigned char sqlcaid[8];//存储一个字符串常量:’SQLCA’。
long sqlabc;//sqlca的长度,固定为136
long sqlcode;//SQL执行后的返回的结果代码
short sqlerrml;//sqlerrmc中返回信息的实际长度
unsigned char sqlerrmc[70];SQL执行后返回的错误或警告信息
unsigned char sqlerrp[8];//诊断数据,用于IBM技术支持人员进行诊断的参考,对其他人无意义
long sqlerrd[6];//此中有两个元素有意义:sqlerrd[2]表示对少个记录行被操作,sqlerrd[4]表示有多少个记录被触发器修改或被外关键字限制
unsigned char sqlwarn[11];//sqlwarn[0]若为0表示无警告信息,w表示有警告信息
unsigned char sqlstate[5];//表明sql语句执行的结果
};
参考资料
• DB2联机文档《Application Development Guide》
• DB2联机文档《应用程序构建指南》
• DB2联机文档样本程序
• 《DB2开发人员指南》(《DB2 Developer's Guide,Fourth Edition 》) Craig S.Mullins(美)机械工业出版社2002年1月出版
发表于 @ 2009年02月20日 15:13:00 | 评论( loading... ) | 举报| 收藏