一、什么是Pro*c程序
在oracle数据库管理系统中,有三种访问数据库的方法:
1.用 SQLPlus,直接输入sql命令以命令行交换的方式访问数据库
2.用一些应用开发工具来访问数据库(PL/SQL developer)
3.利用在语言内嵌入sql语言或者调用oracle库函数来访问数据库,例如Proc
ProC把过程化语言c和非过程化语言sql最完善的结合起来,具有完备的过程处理能力,又能完成任何数据库的处理任务,使用户可以通过编程完成各种类型的报表。在ProC程序中嵌入sql语言,利用这些sql语言可以完成动态地建立、修改和删除数据库中的表,也可以查询、插入、修改和删除数据库表中的行,还可以实现事务的提交和回滚。
在Pro*C程序中还可以嵌入PL/SQL块,用来改进应用程序的性能,特别是在网络环境下,可以减少网络传输和处理的总开销。
二、Pro*C程序的组成
通俗的讲,Pro*C程序实际是内嵌有sql语句或者PL/SQL块的c程序,所以实际上还是c程序,只是比c程序多了一些不同的成分。
每一个Pro*C程序都包含两部分:应用程序首部和应用程序体。
三、Pro*C程序的编译环境
1.使用c编译器gcc编译。
2.Pro*C预编译器,它使用预编译技术,预编译器将源代码中的sql语句转换为标准的oracle库函数调用,从而生成c源程序,再经过gcc编译、链接后生成可执行文件。该预编译器是oracle自带的。
3.c语言头文件和函数库,在 oracle跟目录\product\10.2.0\client_1\precomp目录下
四、Pro*C的程序开发
1.说明SQL通讯区
SQL通讯区用来记录执行每一个嵌入SQL语句的状态信息,通过在函数体外使用以下语句实现:
#include <sqlca.h> 或者 EXEC SQL INCLUDE sqlca;
SQLCA是一个结构类型的变量,它是oracle和应用程序的一个接口。在执行Pro*C程序时,oracle把每一个嵌入SQL语句执行的状态信息存入SQLCA中,根据这些信息,可判断SQL语句的执行是否成功,处理的行数,错误信息等,结构体组成如下所示:
struct sqlca
{
char sqlcaid[8]; //标识通讯区
long sqlabc; //通序区的长度
long sqlcode; //最近执行的SQL语句的状态码
struct{
unsigned sqlerrml;//信息文本长度
char sqlerrmc[10];//错误正文
}sqlerrm;
char sqlerrp[8];
char sqlerrd[6];//当前oracle的状态,sqlerrd[2]才有意义,表示DML语句处理的行数
char sqlwarn[8];//提供可能遇到的条件信息
char sqlext[8];
};
struct sqlca sqlca;
sqlcode的值如下:
等于0 表示SQL语句被正确执行
大于0 oracle执行了该语句,但遇到一个例外(例如没有找到任何数据)
小于0 表示由于数据库、系统、网络或者应用程序的错误,oracle未执行该语句,当出现这一类错误时,当前事务一般应该回滚。
2.声明宿主变量,这里的宿主主要指的就是C
这些变量是应用程序与oracle通信的桥梁,应用程序的输入数据通过C变量传递给oracle,反之,oracle的输出数据又通过C变量传递给应用程序。
如下declare描述部分所示:
EXEC SQL BEGIN DECLARE SECTION;
char szUsername[16];
VARCHAR varPassword[16];
char *szStmt1 = "CREATE TABLE USERS(username VARCHAR2(15) NOT NULL,password VARCHAR2(15) NOT NULL)";
char *szStmt2 = "SELECT password FROM USERS WHERE username='chen'";
EXEC SQL END DECLARE SECTION;
在declare说明段能为SQL变量指定的数据类型如表所示:
CHAR 单个字符
CHAR(n) 字符串
INT 整数
SHORT 短整数
LONG 长整数
FLOAT 单精度浮点数
DOUBLE 双精度浮点数
VARCHAR 变长字符串
NUMBER(m,n) m表示的是所有有效数字的位数,n表示的是小数位的位数。m的范围是1-38,当n=0时表示整数.
值得注意的是:在SQL语句中使用C变量时,前面需要添加冒号,例如上面的变量应该表示为:szUsername。其中VARCHAR为C扩展数据类型,预编译时,Pro*C预编译器将它扩展为一个C结构类型:
struct
{
unsigned short len;
unsigned char arr[16];
}varNo;
在SQL语句中使用VARCHAR类型变量时,只需指出结构名称varPassword就可以了,但在C语句中使用VARCHAR类型变量时,必须具体说明所操作变量的结构元素名称是varPassword.len还是varPassword.arr。另外,如果用VARCHAR类型变量作为函数参数的话,只能用指针形式。
3.连接数据库
EXEC SQL CONNECT :username/password@DBname;
EXEC SQL CONNECT :username IDENTIFIED BY :password;
通过sqlca.sqlcode的值来判断连接数据库是否成功。
4.执行SQL语句
静态SQL语句是指在开发应用程序时就已经明确了的数据库操作,如:
EXEC SQL SELECT password INTO :varPassword FROM USERS WHERE username=:szUsername;
动态SQL语句是在运行时由外部数据提供的,不能直接在C程序中嵌入sql语句,但是可以调用放在一个字符串变量里的SQL语句,最简单的方法是:
EXEC SQL EXECUTE IMMEDIATE :szStmt1;
但这样执行的SQL语句不能实现查询,实现查询并返回数据有两种情况:
第一种是执行静态的查询sql,例如:
EXEC SQL DECLARE SELECT_STMT CURSOR FOR
SELECT * FROM USERS;
EXEC SQL OPEN SELECT_STMT;
EXEC SQL FETCH SELECT_STMT INTO :szUserName,:szPassword;
EXEC SQL CLOSE SELECT_STMT;
第二种是动态的查询sql,假设c组装好的动态sql语句存放在变量szSql[]中,那么过程如下:
EXEC SQL PREPARE D_SELECT_STMT FROM :szSql;
EXEC SQL DECLARE SELECT_STMT CURSOR FOR D_SELECT_STMT;
EXEC SQL OPEN SELECT_STMT;
EXEC SQL FETCH SELECT_STMT INTO :szUserName,:szPassword;
EXEC SQL CLOSE SELECT_STMT;
每行命令具体意思见后面第七节。
5.提交或者回滚所做的数据库处理,并退出数据库
//回滚:
EXEC SQL ROLLBACK WORK RELEASE;
//提交:
EXEC SQL COMMIT WORK RELEASE;
注意语句中的RELEASE选项,它要求关闭所有打开的游标,之后断开与数据库服务器的连接。
五、最终文件的生成
下面是最常用的预编译命令:
#proc iname=example.c INCLUDE=path CODE=ANSI_C MODE=ANSI CPP_SUFFIX=cc SQLCHECK=SEMANTICS USERID=username/password@DBname
预编译后生成example.cc文件就可以当作普通的C源文件来进行处理了。
#gcc -o example -l. -i /oracle/product/10.0.0.2/precomp/public example.cc
最终生成的example文件就是可执行文件了。
六、SQL变量的补充说明
1.指示器变量的说明和引用
指示变量实际上也是一类SQL变量,它被用来管理与其相关联的宿主变量(即在SQL语句中充当输入和输出的变量)。每一个宿主变量都可定义一个指示器变量,主要用于处理空值。指示器变量的说明同一般SQL变量一样,但必须定义成2字节的整形,如SHORT。在SQL语句中引用时,前面也应该加“:“,而且必须附在其相关联的宿主变量之后,在C语句中,可独立使用。当指示器变量为-1时,表示空值。例如:
EXEC SQL BEGIN DECLARE SECTION;
INT dept-number;
SHORT ind-num;
CHAR dept-name[32];
EXEC SQL END DECLARE SECTION;
scanf("%d %s",&dept-number,dept-name);
if (dept-number == 0)
ind-num = -1;
else
ind-num = 0;
EXEC SQL INSERT INTO DEPT(DEPTNO,DNAME) VALUES(:dept-number,ind-num,:dept-name);
其中ind-num是dept-number的指示器变量。当输入的dept-number值是0时,则向DEPT表的DEPTNO列插入空值。
2.指针SQL变量的说明和使用
指针SQL变量在引用前也必须在DECLARE部分先说明。其说明格式同C语言。在SQL语句中引用时,指针名字前要加前缀“:”而不是加“*”。
C语句中就跟C语言的指针变量一样。
3.数组SQL变量的说明和引用
在SQL语句中引用数组时,只需要写数组名(名字前面加冒号),不需要写下标,在C语句中用法如同C语言的数组变量。
使用数组可大大降低网络传输开销。如要向一表插入100行数据,如果没有数组,就要重复100次,而引用后,只需要执行一次插入语句就行。例如:
EXEC SQL BEGIN DECLARE SECTION;
int emp_number[100];
char emp_name[100][15];
float salary[100],commission[100];
int dept_number;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT EMPNO,ENAME,SAL,COMM INTO
:emp_number,:emp_name,:salary,:commission
FROM EMP WHERE DEPTNO=:dept_number;
在使用数组时要注意以下几点:
* 不支持指针数组;
* 只支持一维数组,即emp_name[100][15]被看作一维字符串;
* 数组最大元素个数为32767;
* 在一条SQL语句中引用多个数组时,这些数组维数应该相同;
* 在VALUES,SET,INTO或者WHERE子句中,不允许把简单SQL变量与数组SQL变量混用;
* 不能在declare部分初始化数组。
4.varchar变量在c语言中使用
decalre中定义一个VARCHAR book_name[50];
在c语言中转为:
struct
{
unsigned short len;
unsigned char arr[50];
}book_name;
//使用时:
scanf("%s",book_name.arr);
book_name.len = strlen(book_name.arr);
七、数据库查询以及游标的使用
查询通常有两种:
返回单行或者定行数的查询(前面已经有涉及,这里不再说明了);
返回多行的查询,这种查询要求使用游标来控制每一行或者每一组
1.多行查询及游标的使用
如果查询返回多行或者不知道返回多少行,使用带有oracle游标的select语句。
游标是oracle和PRO*C存放查询结果的工作区域。一个游标与一条SELECT语句相关联。操作游标有四条命令:
DECLARE CURSOR;
OPEN CURSOR;
FETCH;
CLOSE CURSOR;
A.定义游标
一个游标必须首先定义,然后才能使用它。语法如下:
EXEC SQL DECLARE <游标名> CURSOR FOR
SELECT <列>
FROM <表>
//例如:
EXEC SQL DECLARE CSOR CURSOR FOR
SELECT ENAME,JOB,SAL
FROM EMP
WHERE DEPTNO=:deptno;
当赋给一个与查询相关联的游标CURSOR之后,当SELECT查询EMP时可从数据库中返回多行,这些行就是CURSOR的一个活动区域。
注意:
定义游标必须在对游标操作之前完成;
PRO*C不能引用没有定义的游标;
游标定义后,其作用范围是整个程序。所以不能定义两个相同的游标。
B.打开游标
打开游标的OPEN语句主要用来输入主变量的内容,这些主要是WHERE中使用的主变量。打开游标的语句是:
EXEC SQL OPEN CURSOR;
当打开游标后,可以从相关的查询中取出多余一行的结果。所有满足查询标准的行组成一个集合,叫做游标活动集。通过取操作,活动集中的每一行或者每一组是一个一个返回的,查询完成后,游标就可以关闭了。
注意:
游标处于活动集的第一行前面;
若改变了输入主变量就必须重新打开游标。
C.取数据
从活动集中取出一行或一组把结果送到输出主变量中的过程叫取数据。输出主变量的定义在取数据语句中。取数据的语句如下:
EXEC SQL FETCH <游标名> INTO :主变量1,主变量2,...
注意:
FETCH语句每执行一次,从当前行或者当前组取数据一次,下一行或者下一组向上移动一次。游标每次所指的行或者组都为当前行或者当前组,而FETCH每次都是取游标所指定的行或者组的数据;
当游标活动集空之后,oracle返回一个sqlca.sqlcode=1403;
若想再次使用该游标,必须先关闭再打开它;
在C程序中可以开辟一个内存空间,来存放操作结果,这样就能利用开辟的空间来灵活操作查询的结果。
D.关闭游标
EXEC SQL CLOSE <游标名>;
八、SQL嵌套的方法以及应用
嵌入SQL与交互式SQL在形式上有如下差别:
1)在SQL语句前增加前缀“EXEC SQL”, 这一小小的差别其目的是在于预编译时容易识别出来,以便把每一条SQL作为一条高级语言来处理。
2)每一SQL语句分为说明性语句和可执行语句两大类。可执行语句又分为数据定义、数据控制、数据操纵、数据检索四大类。
可执行性SQL语句写在高级语言的可执行处;说明性SQL语句写在高级语言的说明性的地方。
九、错误检测和恢复
在使用SQL语句和PRO*C对数据库进行操作时,常常有字段空值,无条件删除,无行返回,数据溢出和截断等现象发生,这种现象可以用sqlca和指示器变量来检测。
1.错误的处理
所有的SQL语句都有可能出错,所以都要加以判断,但每个SQL语句后都加错误判断,太麻烦了,可以用一个函数来进行处理。
如下:
void sql_error();
EXEC SQL WHENEVER SQLERROR DO sql_error();
这样当发生sqlca.sqlcode < 0的错误时,程序自动转到sql_error中执行,而sqlcode>0的错误是不会自动转到 sql_error函数执行的。
十、PRO*C编译的一个makefile例子
ORA_INC=-I$(ORACLE_HOME)/precomp/public -I$(ORACLE_HOME)/rdbms/public -I$(ORACLE_HOME)/plsql/public -DPRECOMP -DKGDB_ORACLE
ORA_LIBDIR=-L$(ORACLE_HOME)/lib -L$(ORACLE_HOME)/rdbms/lib -L$(ORACLE_HOME)/precomp/lib -L$(ORACLE_HOME)/sqlplus/lib -L$(ORACLE_HOME)/otrace/lib
ORA_LIBS=-lclntsh
PROC=proc
PROCFLAGS=DBMS=v6_char
#cpp_suffix是proc执行生成的c语言源文件
#parse=none表示不对非sql代码进行语法分析,默认对非sql代码也做语法分析
ORACLE_FLAGS = def_sqlcode=yes release_cursor=yes sqlcheck=syntax ireclen=256 parse=none code=cpp cpp_suffix=cc DBMS=V8 unsafe_null=yes char_map=string
CC=g++
CFLAGS=$(ORA_INC) -I. -I/usr/local/include
PROGS=ProcTest
CODE=$(wildcard *.c)
OBJS=$(patsubst %.c,%.o,$(CODE))
all:$(PROGS)
%.cc:%.c
$(PROC) $(ORACLE_FLAGS) iname=$^
#这一步死活不行,不知道为啥
%.o:%.cc
$(CC) -c $(CFLAGS) $< -o $@
$(OBJS):ProcTest.cc
$(CC) -c $(CFLAGS) $< -o $@
$(PROGS):$(OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(ORA_LIBDIR) $(ORA_LIBS) -lm
clean:
rm -f *.o *.lis *.C *.cc $(PROGS) tp*
十一、proc中调用存储过程的方法
假设up_db_emp是一个存储过程,empno,ename是输入参数,return,errno,errtext为输出参数,c_return、c_error、c_errtext为c变量,用于存放输出结果,那么如下:
EXEC SQL EXECUTE
BEGIN
up_db_emp(:empno,:ename,return,error,errtext);
:c_return := return;
:c_error := error;
:c_errtext := errtext;
END;
END-EXEC;