本文来源于网页设计爱好者web开发社区http://www.html.org.cn收集整理,欢迎访问。一 pro*c 程序概述:
1.什么是pro*c程序
在oracle数据库管理和系统中, 有三种访问数据库的方法;
(1) 用sql*plus, 它有sql命令以交互的应用程序访问数据库;
(2)用第四代语言应用开发工具开发的应用程序访问数据库,这些工具有sql*froms,ql*reportwriter,sql*menu等;
(3) 利用在第三代语言内嵌入的sql语言或oracle库函数调用来访问。
pro*c就属于第三种开发工具之一, 它把过程化语言c和非过程化语言sql最完善地结合起来,
具有完备的过程处理能力,又能完成任何数据库的处理品任务,使用户可以通过编程完成各种类型的报表。在pro*c程序中可以嵌入sql语言,
利用这些sql语言可以完成动态地建立、修改和删除数据库中的表,也可以查询、插入、修改和删除数据库表中的行, 还可以实现事务的提交和回滚。
在pro*c程序中还可以嵌入pl/sql块, 以改进应用程序的性能, 特别是在网络环境下,可以减少网络传输和处理的总开销。
2.pro*c的程序结构图
通俗来说,pro*c程序实际是内嵌有sql语句或pl/sql块的c程序, 因此它的组成很类似c程序。 但因为它内嵌有sql语句或pl/sql块,
所以它还含有与之不同的成份。为了让大家对pro*c有个感性的认识, 特将二者差别比较如下:
c的全程变量说明
c源程序 函数1:同函数k。
函数2:同函数k。
c的局部变量说明
函数k
可执行语句
应用程序首部 c的外部变量说明
外部说明段(oracle变量说明)
通讯区说明
pro*c源程序 函数1:同函数k。
函数2:同函数k。
c局部变量说明
程序体 内部说明部分 内部说明段
通讯区说明
函数k c的可执行语句
可执行语句 sql的可执行语句
或pl/sql块
二.pro*c程序的组成结构
每一个pro*c程序都包括两部分:(1)应用程序首部;(2)应用程序体
应用程序首部定义了oracle数据库的有关变量,
为在c语言中操纵oracle数据库做好了准备。应用程序体基本上由pro*c的sql语句调用组成。主要指查询select、insert、update、delete等语句。
应用程序的组成结构如图所示:
应用程序首部
描述部分
sql通信区
应用程序体
exec sql begin declare section
(sql变量的定义)
exec sql end declare section;
exec sql include sqlla;
exec sql connect:< 用户名>
identified by: < 口令 >
sql 语句及游标的使用
1. 应用程序首部
应用程序的首部就是pro*c的开始部分。它包括以下三部分:
l c变量描述部分;
l sql变量描述部分(declare部分);
l sql通信区。
(1) .declare部分(描述部分)
描述部分说明程序的sql变量, 定义部分以exec sql begin declare section ;开始和以 exec sql end declare
section ;结束的。它可以出现在程序的主部,也可出现在局部
l sql变量的说明和使用
在说明段能为sql变量指定的数据类型如表所示:
数据类型描述
char
char(n)
int
short
long
float
double
varchar单字符
n个字符数组
整数
短整数
单精度浮点数
双精度浮点数
变长字符串
这些数据类型实际上就是c语言的数据类型, 其中varchar中视为c数据类型的扩充。这在以后会谈到。
sql变量的使用应注意以下几点:
l 必须在描述部分明确定义
l 必须使用与其定义相同的大小写格式
l 在sql语句中使用时,必须在其之前加一个“:”(冒号),但在c语句中引用时不需加冒号。
l 不能是sql命令中的保留字。
l 可以带指示变量。
例如:exec sql begin declare sections;
varchar programe[30];
int porgsal, pempno;
exec sql end declare section;
exec sql select ename , sal
into: programe, : progsal
from emp
where empno = : pempno;
(2). 指示器变量的说明和引用
指示变量实际上也是一类sql变量,它被用来管理与其相关联的宿主变量(即在sql语句中充
当输入或输出的变量)。每一个宿主变量都可定义一个指示器变量,主要用于处理空值(null)
指示器变量的说明基本同一般sql变量一样, 但必须定义成2字节的整型,如short、int。在sql语句中引用时,
其前也应加“:”(冒号),而且必须附在其相关联的宿主变量之后,在c语句中,可独立使用。当指示器变量为-1时,表示空值。例如:
exec sql begin declare section ;
int dept- number;
short ind – num;
char emp –name;
exec sql end declare section ;
scanf(“90d %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列插入空值。
(3).指针sql变量的说明和使用
指针sql变量在引用前也必须在declare
部分先说明。其说明格式同c语言。在sql语句中引用时,指针名字前要加前缀“:”(冒号)而不加“*”(星号)。在c语句中用法如同c语言的指针变量。
(4).数组sql变更的说明和引用
在sql语句中引用数组时,只需写数组名(名字前加冒号), 不需写下标,在c语句中用法如同c语言的数组变量。
使用数组可大大降低网络传输开销。如要向一表插入100行数据,如果没有数组,就要重复100次, 而引用后,只须执行一次insert语句、便可一次性插入。例如:
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;
在使用数组时,应注意以下几点;
l 不支持指针数组
l 只支持一维数组, 而 emp-name [100][15]视为一维字符串
l 数组最大维数为32767
l 在一条sql语句中引用多个数组时,这些数组维数应相同
l 在values , set, into 或where子名中, 不允许把简单sql变量与数组sql变量混用
l 不能在delare部分初始化数组
例如:下面的引用是非法的
exec sql begin declare section;
int dept – num [3] = {10,20,30};
exec sql end declare section ;
exec sql select empno, ename , sal
into : emp – num [ i ], : emp – name [ i ], : salarg [ i ]
from emp
(5) 伪类型varchar的说明和引用
varchar变量在引用之前也必须在说明段说明, 说明时必须指出串的最大
长度,如:
exec sql begin declare section;
int book – number;
varchar book – name [ 50 ];
exec sql end declare section ;
在预编绎时, book – name 被翻译成c语言中的一个结构变量;
struct { unsigned short len ;
unsigned chart arr [ 20 ] ;
} boo – name
由此看出, varchar变量实际上是含长度成员和数组成员的结构变量。在sql语句中引用时,应引用以冒号为前缀的结构名, 而不加下标,在c语句 中引用结构成员。
varchar变量在作输出变量时,由oracle自动设置, 在作为输入变量时,程序应先把字符串存入数组成员中,
其长度存入长度成员中,然后再在sql语句中引用。例如:
main( )
{ .......
scanf(“90s, 90d’, book – name .arr, & book – number );
book – name .len = strlen (book – name .arr);
exec sql update book
set bname = : book – name ;
bdesc = : book – number ;
}
(6) sql通信区
sql 通信区是用下列语句描述的:
exec sql include sqlca;
此部分提供了用户运行程序的成败记录和错误处理。
sqlca的组成
sqlca是一个结构类型的变量,它是oracle 和应用程序的一个接口。在执行 pro*c程序时, oracle
把每一个嵌入sql语句执行的状态信息存入sqlca中, 根据这些信息,可判断sql语句的执行是否成功,处理的行数,错误信息等,其组成如表所示:
struct sqlca
{ char sqlcaid [ 8 ] ; ----à标识通讯区
long sqlabc; ---à 通讯区的长度
long sqlcode; ---à保留最近执行的sql语句的状态码
struct { unsigned short sqlerrml; -----à信息文本长度
}sqlerrm;
char sqlerrp [ 8 ];
long sqlerrd [ 6 ];
char sqlwarn [ 8 ];
char sqlext [ 8 ];
}
struct sqlca sqlca;
其中, sqlcode在程序中最常用到,它保留了最近执行的sql语句的状态码。程序员根据这些状态码做出相应的处理。这些状态码值如下:
0: 表示该sql语句被正确执行,没有发生错误和例外。
>0:oracle执行了该语句,但遇到一个例外(如没找到任何数据)。
<0:表示由于数据库、系统、网络或应用程序的错误,oracle未执行该sql语句。
当出现此类错误时,当前事务一般应回滚。
2.应用程序体
在pro*c程序中, 能把sql语句和c语句自由地混合书写,并能在sql语句中使用sql变量,嵌入式sql语句的书写文法是:
l 以关键字exec sql开始
l 以c语言的语句终结符(分号)终结
sql语句的作用主要用于同数据库打交道。c语言程序用于控制,输入,输出和数据处理等。
(1) 连接到oracle数据库
在对数据库存取之前,必须先把程序与oracle数据库连接起来。即登录到oracle上。所连接命令应该是应用程序的第一个可执行命令。连接命令格式如下:
exec sql connect:< 用户名 > identified by : < 口令 >
或exec sql connect: < 用户名 > / < 口令 >
在使用上述两种格式进行登入时, 应当首先在说明段定义包含用户名和口令的
sql 变量,并在执行connect之前设置它们,否则会造成登录失败。例如:
exec sql begin declare section ;
varchar usename [20];
varchar password[20];
exec sql end declare
..........
strcpy ( usename.arr, “csott’);
usename.len = strlen (username.arr);
strcpy (password.arr , “tiger’);
password .len = strlen( password .arr);
exec sql whenever sqlerror goto sqlerr;
exec sql connect :username indntified by : password;
注意: 不能把用户名和口令直接编写到connect语句中,或者把用引号(’)括起来的字母串在connect 语句中, 如下面的语句是无效的。
exec sql connect scott inentified by tiger;
exec sql connect ‘scott’ identified by ‘tiger’;
(2). 插入、更新和删除
在讲述sql语言时已详细讲过, 这里就不举例说明了。
(3). 数据库查询及游标的使用
在pro*c中, 查询可分为两种类型:
l 返回单行或定行数的查询;
l 返回多行的查询.此种查询要求使用游标来控制每一行或每一组(主变量用数组).
1) 返回单行或定行数的查询
在pro*c中的查询sql select语句由以下几个子句组成:
select
into
from
where
connect by
union
intersect
minus
group by
having
order by
其中where子句中的查询条件可以是一个属性或多个属性的集合,在执行是赋值的主变量也可放在where子句中.where子句中所用的主变量称为输入主变量。如:
select empno, job, sal
into:pname, :pjob, :psal
from emp
where empno=:pempno;
若没有找到限定的行, 则sqlca.sqlcode返回”+1403”, 表明”没有找到”。
into从句中的主变量叫输出主变量,它提供了查询时所需要的信息。
在任何项送给主变量之前,都要求oracle把这些项转换成主变量的数据类型。对于数字是通过截断来完成的(如:9.23转换为9)。
如果已确定查询只返回一行,那么就不必使用游标,只给select语句增加一个into子句即可。在语义上into语句在from之前的查询中有多少个选择项就有多少个输出主变量。若在select项中表达式的数目不等于into子句中主变量的数目,就把sqlca.sqlwarn[3]置为”w”。
2)多行查询及游标的使用
如果查询返回多行或不知道返回多少行,使用带有oracle游标(cursor)的select语句。
游标是oracle和pro*c存放查询结果的工作区域。一个游标(已命名的)与一条select语句相关联。操作游标有由4条命令:(1)declare
cursor;(2)open cursor;(3)fetch;(4)close cursor。
a. 定义游标
一个游标必须首先定义, 才能使用它。语法为:
exec sql declare 〈游标名〉corsor for
select 〈列〉
from 〈表〉
例如:
exec sql declare csor, cursor for
select ename , job, sal
from emp
where deptno=:deptno;
当赋给一个与查询相关联的游标cursor之后, 当select查询emp时可从数据库中返回多行,这些行就是cursor的一个活动区域。
注意:
1) 定义游标必须在对游标操作之前完成;
2) pro*c不能引用没有定义的游标;
3) 游标定义后,其作用范围是整个程序。所以对一个程序来讲, 同时定义两个相同的游标是错误的。
b. 打开游标
打开游标的open语句主要用来输入主变量的内容,这些主要是where中使用的主变量。打开游标的语句是:exec sql open 〈游标名〉
当打开游标后,可以从相关的查询中取出多于一行的结果。所有满足查询标准的行组成一集合,叫做“游标活动集”。通过取操作,活动集中的每一行或每一组是一个一个返回的,查询完成后,
游标就可关闭了。如图所示:
定义游标:declare
开始查询:select
打开游标:open
从活动集取数据:fetch
查询完成
关闭游标:close
注意:1)游标处于活动集的第一行前面;
2)若改变了输入主变量就必须重新打开游标。
c. 取数据
从活动集中取出一行或一组把结果送到输出主变量中的过程叫取数据。输出主变量的定义在取数据语句中。取数据的语句如下:
exec sql fetch〈游标名〉into:主变量1,主变量2,……
fetch的工作过程如图所示:
查询结果
游标
fetch
查询结果
在游标打开后
输出至当前
……
如图所示的查询结果指满足查询条件的查询结果。使用fetch应注意以下几点:
l 游标必须先定义再打开。
l 只有在游标打开之后才能取数据,即执行fetch语句。
l
fetch语句每执行一次,从当前行或当前组取数据一次,下一行或下一组向上移一次。游标每次所指的行或组都为当前行或当前组,而fetch每次都是取游标所指定的行或组的数据。
l 当游标活动集空之后,orcle返回一个sqlca。sqlca(=1403)。
l 若希望此游标再操作, 必须先关闭再打开它。
l 在c程序中可以开辟一个内存空间,来存放操作结果,这样就能利用开辟的空间来灵活操纵查询的结果。
d.关闭游标
取完活动集中所有行后,必须关闭游标,以释放与该游标有关的资源。
关闭游标的格式为:
exec sql close 游标名;
例如:
exec sql close c1;
oracle v5.0版支持sql格式“current of cursor”。这条语句将指向一个游标中最新取出的行,
以用于修改和删除操作。该语句必须有取操作之后使用,它等同存储一个rowid,并使用它。
(4).举例
exec sql declare salespeople cursor for
select ssno, name, salary
from employee
where dname=‘sales’;
exec sql open salespeople;
exec sql fetch salespeople
into :ss,:name,:sal;
exec sql close salespeople;
(5)sql嵌套的方法及应用
嵌入sql与交互式sql在形式上有如下差别:
1) 在sql语句前增加前缀“exec sql”, 这一小小的差别其目的是在于预编译时容易识别出来,
以便把每一条sql作为一条高级语言来处理。
2) 每一sql语句分为说明性语句和可执行语句两大类。可执行语句又分为数据定义、数据控制、数据操纵、数据检索四大类。
可执行性sql语句写在高级语言的可执行处;说明性sql语句写在高级语言的说明性的地方。
例如:在pro*c程序中建立一个名为book的表结构,过程如下:
#include〈stdio.h〉
exec sql begin declare section;
varchar uid[20], pwd[20];
exec sql end declare section;
exec sql include sqlca;
main()
{
/*login database*/
strcpy(uid.arr,’wu’);
uid.len=strlen(uid,arr);
strcpy(pwd.arr,’wu’);
pwd.len=strlen(pwd.arr);
exec sql connect:uid identifeed by:pwd;
exec sql create table book
( acqnum number, copies number , price number);
exec sql commit work release;
exit;
pro*c可非常简便灵活地访问orcle数据库中的数据,同时又具有c语言高速的特点,因而可完成一些oracle产品不能完成的任务,例如以下一个固定的特殊格式输出结果。
sql嵌套源程序示例
#unclude
typedef char asciz[20];
exec sql begin declare section;
exec sql type asciz is string (20) reference;
asciz username;
asciz password;
asciz emp_name(5);
int emp_number(5a);
float salary[5];
exec sql end declare section;
exec sql include sqlca;
void print_rows();
void sqlerror();
main()
{
int num_ret;
strcpy(username,”scott’);
strcpy(password, “tyger”);
exec sql whenever sqlerror do sqlerror();
exec sql connect:username identified by:password;
print (“/nconnected to oracle as user:%s/n”, username);
exec sql declare c1 cursor for
select empno , ename , sal from emp;
exec sql open c1;
num_ret = 0;
for(;;)
{
exec sql whenever not found do break;
exec sql fetch c1 into : emp_number , :emp_name , :salary;
print_rows (sqlca.sqlerrd[2] – num_ret);
num_ret=sqlca.sqlerrd[2];
}
if ((sqlca.sqlerrd[2] – num_ret)>0);
print _rows(sqlca.sqlerrd[2] –num_ret);
exec sql close c1;
printf(“/have a good day./n”);
exec sql commit work release;
}
void print_rows(n);
int n;
{
int i;
printf(“/nnumber employee salary/n”);
printf(“------------------------------/n”);
for (i=0;i printf(“% - 9d%- 8s%9.2f/n”,emp-number[i], emp---name[i],salary[i];
}
void sqlerror()
{
exec sql whenever sqlerror continue;
printf(“/noracle error detected:/n”);
printf(‘/n%.70s/n”, sqlca.sqlerrm.sqlerrmc);
exec sql rollback work release;
exit(1);
}
(6) 错误检测和恢复
在使用sql语句和pro*c对数据库进行操作时,常常有字段空值,无条件删除,无行返回,数据溢出和截断等现象发生,这种现象可以用sqlca和指示器变量来检测。
1 sqlca的结构
在pro*c程序中sqlca结构如下:
struct sqlca{
char sqlcaid[8];
long sqlabc;
long sqlcode;
struct{
unsigned sqlerrm1;
char sqlerrmc[10];
}sqlerrm;
char sqlerrp[8];
long sqlerrd[6];
char sqlwarn[8];
char sqlext[8];
}
其中:
1) sqlca.sqlerrm.sqlerrmc:带有sqlca。sqlcode的错误正文。
2) sqlca.sqlerrd:当前oracle的状态,只有sqlca.sqlerrd[2]有意义,表示dml语句处理的行数。
3) sqlca.sqlwarn:提供可能遇到的条件信息。
在每执行一个sql语句后,oracle就把返回结果放入sqlca中,但说明语句除外。
用sqlca可以查看sql语句的执行结果。往往有三种结果:
=0:执行成功;
sqlca.sqlcode= >0:执行成功的状态值;
<0:失败,不允许继续执行。
2 指示器变量
指示器变量有时也称指示变量.指示变量与一个主变量相关联,指出主变量的返回情况.
=0:返回值不为空, 未被截断,值放在主变量中;
返回值= >0:返回值为空, 忽略主变量的值;
<0:主变量长度不够就被截断。
使用指示变量要注意:
l 在where子句中不能用指示变量。用null属性来测试空值。
例如下列子句:
select…
from…
where ename is null;
是正确的,而
where ename=:peme:peme1
是错误的。
l 指示变量在插入空值之前为—1
l 可输出空值。
3 whenever语句
whenever是说明语句,不返回sqlcode, 只是根据sqlca中的返回码指定相关的措施。格式为
exec sql whenever [sqlerror|sqlwarning|notforund]
[stop|continue|goto<标号>];
其中
(1)[stop|continue|got<标号>]的缺省值为continue。
(2)sqlerror:sqlca.sqlcode<0;
(3)sqlwarnign:sqlca.sqlwarn[0]=“w”;
(4)notfound:sqlca.sqlcode=1403;
下面给出一段程序来说明whenever的用法:
exec sql begin deelare section;
varchar uid[20];
varchar pasw[20];
……
exec sql end declare section;
exec sql include sqlca;
main()
{
……
exec sql whenever sqlerror goto err;
exec sql connect:uid/:pwd;
……
exec sql declare csor1 cursor for
select 〈字段〉
form〈表〉
exec sql open csor1;
sql
……
exec sql whenever not found goto good;
for(;;)
exec sql fetch csor, into……
good:
……
printf(“/n查询结束/n”);
exec sql close c1;
exec sql whenever sqlerror continue.
exec sql commit work release:
exit();
printf(“/n%70s|n”, sqlca.sqlerrm.sqlerrmc);
exec sql rollback work release:
exit(1);
}
(7) 动态定义语句
sql语句分动态定义语句和静态定义语句两种:
(1) 静态定义语句:sql语句事先编入pro*c中,在经过预编译器编译之后形成目标程序*。boj,然后执行目标程序预即可。
(2) 动态定义语句:有些语句不能事先嵌入到pro*c程序中,要根据程序运行情况,用户自己从输入设备上(如终端上)实时输入即将执行的sql语句。
动态定义语句有:
l execute immediate;
l prepare 与execute;
l prepare与fetch 和 open ;
l bind与define descriptor。
1. execute immediate语句
此语句表示立即执行, 并且只向sqlca返回执行结果,无其它信息。例如:
exec sql begin declare section;
varchar abcd[89];
varchar deay[20];
exec sql end declare section;
/** 输出字符串到abcd **/
exec sql execute immediate :abcd;
注意:
1) execute immediate只能运行带一个参数的动态语句。其中,abcd是参数,不是关键字。
2) execute immediate使用的先决条件是:sql语句不能包含主变量;sql语句不能是查询语句。
3) 可用任何主变量作为execute immediate的参数;也可用字符串作为主变量。
2. prepare与execute语句
此语句表示“预编译/执行”。此语句能够预编译一次而执行多次。语法为:
exec sql prepare 〈语句名〉from:主变量;
exec sql execute〈语句名〉[using:替换主变量];
prepare语句做两件事:
(1) 预编译sql语句;
(2) 给出sql语句的语句名。
注意:
l sql语句不能是查询语句;
l prepare和execute可包含主变量;
l prepare不能多次执行。
例如:
#define username “scott”
#define password “tiger”
#include
exec sql include sqlca;
exec sql begin declare section;
char * username=username;
char * password=password;
varchar sqlstmt[80];
int emp_number;
varchar emp_name[15];
varchar job[50];
exec sql end declare section;
main()
{
exec sql whenever sqlerror goto :sqlerror;
exec sql connect :username identified by :password;
sqlstmt.len=sprintf(sqlstmt.arr,”insert into emp (empno,ename,job,sal)
values(:v1,:v2,:v3,:v4)”);
puts(sqlstmt.arr);
exec sql prepare s from :sqlstmt;
for(;;)
{
printf(“/nenter employee number:”);
scanf(“%d”,&emp_number);
if (emp_number==0) break;
printf(“/nenter employee name:”);
scanf(“%s”,&emp_name.arr);
emp_name.len=strlen(emp_name.arr);
printf(“/nenter employee job:”);
scanf(“%s”,job.arr);
job.len=strlen(job.arr);
printf(“/nenter employee salary:”);
scanf(“%f”,&salary);
}
exec sql execute s using :emp_number,:emp_name,:job,:salary;
}
3. fetch语句和open语句
fetch语句和open语句这组动态语句是对游标进行操作的,其执行过程如下:
prepare〈语句名〉from 〈主变量字符串〉;
declare〈游标名〉for〈语句名〉;
open 〈游标名〉[using:替换变量1[,:替换变量变…]]
fetch〈游标名〉into: 主变量1[,:主变量2…]
close〈游标名〉
注意:
l sql语句允许使用查询语句;
l select子句中的列名不能动态改变,只能预置;
l where和order by 子句可以动态改变条件。
一、 pro*c的编译和运行
1.
先用oracle预编译器proc对pro*c程序进行预处理,该编译器将源程序中嵌入的sql语言翻译成c语言,产生一个c语言编译器能直接编译的文件。生成文件的扩展名为
.c
2. 用c语言编译器cc 对扩展名为 .c的文件编译,产生目标码文件,其扩展名为 .o
3. 使用make命令,连接目标码文件,生成可运行文件
例如: 对上面的example.pc进行编译运行
proc iname=example.pc
cc example.c
make exe=example objs=”example.o”
example