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 〈游标名〉
当打开游标后,可以从相关的查询中取出多于一行的结果。所有满足查询标准的行组成一集合,叫做“游标活动集”。通过取操作,活动集中的每一行或每一组是一个一个返回的,查询完成后, 游标就可关闭了。
注意:1)游标处于活动集的第一行前面;
2)若改变了输入主变量就必须重新打开游标。
C. 取数据
从活动集中取出一行或一组把结果送到输出主变量中的过程叫取数据。输出主变量的定义在取数据语句中。取数据的语句如下:
EXEC SQL FETCH〈游标名〉INTO:主变量1,主变量2,……
使用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中,但说明语句除外。
使用指示变量要注意:
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(“/nps|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语句这组动态语句是对游标进行操作的,其执行过程如下:
注意:
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
一 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等语句。
应用程序的组成结构如图所示:
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变量指定的数据类型如表所示:
数据类型 描述
CHARCHAR(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] = ;
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
转载网上的一篇文章支持一下。
PROC是ORACLE数据库提供的编程接口之一,其应用十分的广泛,本文通过一个具体的例子,介绍PROC编程的一些经验及应注意的地方。
例子程序:
#include
#include
#include
#include
#include
EXEC SQL INCLUDE sqlca;
/*RELEASE_CURSOR=YES 使PROC 在执行完后释放与嵌入SQL有关资源*/
EXEC ORACLE OPTION (RELEASE_CURSOR = YES);
EXEC SQL BEGIN DECLARE SECTION;
varchar vc_user[20];
long al_empno=0;
char ac_ename[11]="";
char ac_hiredate[20]="";
double af_sal=0;
EXEC SQL VAR ac_ename IS STRING(11);
EXEC SQL VAR ac_hiredate IS STRING(20);
EXEC SQL END DECLARE SECTION;
/*错误处理函数*/
void sql_error(char *msg)
{
printf("/n%s,%ld,%s/n", msg,sqlca.sqlcode,(char *)sqlca.sqlerrm.sqlerrmc);
EXEC SQL ROLLBACK RELEASE;
exit(-1);
}
main()
{
EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE ERROR: ");
/*连接数据库*/
strcpy(vc_user.arr,"scott/tiger@DEMO");
vc_user.len=16;
exec sql connect :vc_user;
EXEC SQL DECLARE cur_emp CURSOR FOR
SELECT EMPNO, ENAME,to_char(HIREDATE,'yyyy/mm/dd hh24:mi:ss'),SAL FROM EMP;
EXEC SQL OPEN cur_emp;
while(1)
{
al_empno=0;
strcpy(ac_ename,"");
strcpy(ac_hiredate,"");
af_sal=0;
EXEC SQL FETCH cur_emp INTO :al_empno, :ac_ename:ename_ind, :ac_hiredate:hiredate_ind, :af_sal:sal_ind;
if( sqlca.sqlcode == 1403)
{
break;
}
printf("empno=%ld,ename=%s,hiredate=%s,sal=%lf/n",al_empno,ac_ename,ac_hiredate,af_sal);
}
EXEC SQL CLOSE cur_emp;
EXEC SQL ROLLBACK WORK RELEASE;
}
1、宿主变量的声明
在PROC中,在SQL语句中用到的变量称为宿主变量。他们应在EXEC SQL BEGIN DECLARE SECTION;与EXEC SQL EDN DECLARE SECTION;
之间声明,如上面所示.在声明宿主变量时应注意以下几点:
(1) 在数据库表中定义为VARCHAR2,VARCHAR,CHAR的字段,在PROC中可声明为CHAR,但长度应为它们在表中定义的长度加1,因为PROC中
CHAR型变量用做结尾。
如:ENAME在表中的定义为ename varchar2(10),在PROC中可定义为:
EXEC SQL BEGIN DECLARE SECTION;
char ename[11];
EXEC SQL END DECLARE SECTION;
常见错误说明:
如果插入的字符串长度大于10,如:EXEC SQL INSERT INTO EMP(ENAME) VALUES('12345678901');会出现以下错误:
error:ORA-01480: STR 赋值变量缺少空后缀。
如果定义为:
EXEC SQL BEGIN DECLARE SECTION;
char ename[15];
EXEC SQL END DECLARE SECTION;
当插入的字符串长度大于10,小于15时,如:EXEC SQL INSERT INTO EMP(ENAME) VALUES('12345678901');会出现以下错误:
error:ORA-01401: 插入的值对于列过大。
当插入的字符串长度大于15,如:EXEC SQL INSERT INTO EMP(ENAME) VALUES('12345678901234');会出现以下错误:
error:ORA-01401:STR 赋值变量缺少空后缀。
(2) 从SQL语句中取字段的值到宿主变量中时,PROC不会自动给宿主变量去掉右空格。而是以在DECLARE SECTION 中定义的长度为准(与 表中定义的无关)不足补右空格.如果不注意这一点,在PROC中进行字符串操作时(如比较相等)会出错。如:
EXEC SQL BEGIN DECLARE SECTION;
char ename[10];
EXEC SQL END DECLARE SECTION;
如果ENAME在表中的值为'abc',则取出的值为'abc ';
可用语句EXEC SQL VAR重定义CHAR型变量。这样宿主变量会自动去掉右空格。如下:
EXEC SQL BEGIN DECLARE SECTION;
char ename[11];
EXEC SQL VAR ac_ename IS STRING(11);
EXEC SQL END DECLARE SECTION;
如果ENAME在表中的值为'abc',则取出的值为'abc';
(3) 对浮点型的变量,为保证精度,最好是声明成DOUBLE型的.因为DOUBLE型的精度比FLOAT型高很多.
(4) 整型可声明为LONG型(对较长的整型,而且所用的平台支持的话,如在SUN平台上,可声明为LONG LONG型).
(5) DATE型的处理:DATE型一般声明为CHAR(20)。
往表中插入DATE型数据时,一般用TO_DATE()函数进行类型转换,取出值时一般用TO_CHAR()函数进行类型转换.
EXEC SQL select to_char(hiredate,'yyyy/mm/dd hh24:mi:ss') into :ac_hire_date from EMP where empno=1234;
EXEC SQL insert into EMP(EMPNO,HIREDATE) values(123,to_date(:ac_hiredate,'yyyy/mm/dd hh24:mi:ss');
2、宿主变量的作用范围
如果宿主变量在所有的函数之外声明,则他们是全局变量。在使用之前要注意把变量的值初始化,宿主变量也可以在某个函数的内部定义。 这时他们是局部变量。一般都习惯把宿主变量声明为全局变量。
3、数据库的连接与断开
数据库的连接有以下两种方法:
(1)
strcpy(vc_user.arr,"scott/tiger");
vc_user.len=11;
exec sql connect :vc_user;
(2)
strcpy(user,"scott");
strcpy(pass,"tiger");
exec sql connect :user identified by :pass;
注意:在有些平台上两种都可以,在有些平台上只能用第一种方法.
在PROC程序中,要记住用EXEC SQL ROLLBACK WORK RELEASE;断开与数据库的连接,并释放相关的数据库资源。
4、PROC中的NULL值的处理
如果某一字段取出的值是NULL,会报:sqlcode=-1405, sqlerr=ORA-01405: 读取的列值为 NULL
并且相应的宿主变量的值不会被改变,为执行该SQL语句之前的值. 常用的处理NULL值的方法有:
(1)采用指示器变量,此时不会有-1405错误,当必须是所以为NULL的字段都有相应的指示器变量,如果某一字段没有指示器变量,但取出的值
为NULL值,则仍然会有-1405错误.当取出的值是NULL时,相应的指示器变量变量为-1,可根据指示器变量的值做响应的处理。
(2)如果字段较多,可取字段到一个结构体中及与该结构体对应的指示器结构体中.如上面的例子中可定义结构体:
struct str_emp{
long al_empno;
char ac_ename;
char ac_hiredate;
double af_sal;
};
struct str_emp_ind{
long al_empno;
char ac_ename;
char ac_hiredate;
double af_sal;
};
struct str_emp str_emp;
strcut str_emp_ind str_emp_ind;
在取之前可用memset(&str_emp,0,sizeof(str_emp)).清空该结构体,这样如果是字符型的NULL,会为"",整型的NULL会为0,
浮点型的会为0.00。此时不会有-1405错误。
(3)也可采用NVL()函数:举例如下:
EXEC SQL DECLARE authors CURSOR FOR
SELECT EMPNO, NVL(ENAME,chr(0)),nvl(to_char(HIREDATE,'yyyy/mm/dd hh24:mi:ss'),chr(0)),NVL(SAL,0) FROM EMP;
这样也不会有-1405错误不,当取出的值是NULL时,自动用NVL()中指定的值代替.
CHR(0)也可直接用''代替,如下:
SELECT EMPNO, NVL(ENAME,''),nvl(to_char(HIREDATE,'yyyy/mm/dd hh24:mi:ss'),''),NVL(SAL,0) FROM EMP;5、PROC中的错误的处理
所有的SQL语句都有可能出错.所以都要加以判断,但每个SQL语句后都加错误判断,太麻烦,可用一个函数如sql_error()来进行错误处理,
方法:
(1)定义ql_error()函数。
(2)在开头加上EXEC SQL WHENEVER SQLERROR DO sql_error();这样当发生sqlca.sqlcode <0 的错误时,程序自动转到sql_error()中执行. 注意:对sqlca.sqlcode >0的错误如 sqlca.sqlcode =1403 是不会转到sql_error()中执行的.
另外:在UNIX下,可以用OERR 来查找错误的描述。如: ora ORA -1405 查找错误号为-1405的描述.
6、PROC中调用存储过程的方法
要把存储过程放在EXEC SQL EXECUTE 和 END-EXEC;之间,如下所示:
其中:al_empno,ac_ename 为输入参数,l_return,l_errno,c_errtext 为输出参数。
al_empno=8888;
strcpy(ac_ename,"ABCD");
EXEC SQL EXECUTE
BEGIN
up_db_emp(:al_empno,:ac_ename,:l_return,:l_errno,:c_errtext);
END;
END-EXEC;
if (l_return != 0)
{
printf("调用UP_PB_EMP存储过程出错,errno=%ld,errtext=%s/n",l_errno,c_errtext);
}
7、PROC的命令行选项:PROC编译器有很多的命令行选项,在命令行下直接不带参数运行PROC,会列出所有的命令行选项来,并有说明。
(1)储存过程:编译储存过程是要带上用户名及密码
proc USERID=scott/tiger sqlcheck=SEMANTICS ireclen=512 iname=test.cpp
(2)PARSE=NONE 对非SQL代码不进行语法分析,默认对非SQL代码也进行语法分析.
在RED HAD6.3上的ORACLE8.1.5中用PROC时,会提示:/USR/INCLUDE/STDIO.H 及其他的.H文件中有错. 可把PARSE=NONE加上,就好了.
8、注意加上:EXEC ORACLE OPTION (RELEASE_CURSOR = YES);
RELEASE_CURSOR=YES 使PROC 在执行完后释放与嵌入SQL有关资源,保证在该PROC程序执行完后,ORACLE不会锁住数据库资源,如锁表等。
如果在PROC中用到ORACA,还要在程序头加上:
EXEC ORACLE OPTION (ORACA=YES);
9、PROC中的类型转换
一、在C语言中:
(1)字符型到整型可用ATOI() ATOL(),SSCANF()
(2)整型,浮点型到字符型,可用SPRINTF()
(3)字符型到浮点型用ATOF()不行,最好用SSCANF(),举例如下:
EXEC SQL BEGIN DECLARE SECTION;
double d_demo;
float f_demo;
char ac_text[20]="222";
EXEC SQL END DECLARE SECTION;
(1)sscanf(ac_text, "%f", &d_demo);
printf("ac_text=%s,d_demo=%f/n",ac_text,d_demo);
(2)sscanf(ac_text, "%lf", &d_demo);
printf("ac_text=%s,d_demo=%f/n",ac_text,d_demo);
(3)sscanf(ac_text, "%f", &d_demo);
printf("ac_text=%s,d_demo=%lf/n",ac_text,d_demo);
(4)sscanf(ac_text, "%lf", &d_demo);
printf("ac_text=%s,d_demo=%lf/n",ac_text,d_demo);
printf("*******************/n");
(5)sscanf(ac_text, "%f", &f_demo);
printf("ac_text=%s,f_demo=%f/n",ac_text,f_demo);
(6)sscanf(ac_text, "%lf", &f_demo);
printf("ac_text=%s,f_demo=%f/n",ac_text,f_demo);
(7)sscanf(ac_text, "%f", &f_demo);
printf("ac_text=%s,f_demo=%lf/n",ac_text,f_demo);
(8)sscanf(ac_text, "%lf", &f_demo);
printf("ac_text=%s,f_demo=%lf/n",ac_text,f_demo);
输出的结果:
ac_text=222.00,d_demo=0.000000
ac_text=222.00,d_demo=222.000000
ac_text=222.00,d_demo=222.000032
ac_text=222.00,d_demo=222.000000
*******************
ac_text=222.00,f_demo=222.000000
ac_text=222.00,f_demo=0.000000
ac_text=222.00,f_demo=222.000000
ac_text=222.00,f_demo=0.000000
d_demo=atof(ac_text);
printf("ac_text=%s,atof(ac_text)=%f/n",ac_text,d_demo);
d_demo=atof(ac_text);
printf("ac_text=%s,atof(ac_text)=%lf/n",ac_text,d_demo);
f_demo=atof(ac_text);
printf("ac_text=%s,atof(ac_text)=%f/n",ac_text,f_demo);
f_demo=atof(ac_text);
printf("ac_text=%s,atof(ac_text)=%lf/n",ac_text,f_demo);
输出的结果:
ac_text=222.00,atof(ac_text)=1243288.000000
ac_text=222.00,atof(ac_text)=1243288.000000
ac_text=222.00,atof(ac_text)=1243288.000000
ac_text=222.00,atof(ac_text)=1243288.000000
从上面的结果可见:
DOUBLE型应采用sscanf(ac_app_capcity, "%lf", &d_app); 打印用"%lf","%f" 都可以. (2),(4)正确
FLOAT型应采用sscanf(ac_app_capcity, "%f", &d_app); 打印用"%lf","%f" 都可以. (5),(7)正确
采用ATOF()转换的结果都是错的,所以不要用它。
二、写表或从表中取数据时:
(1)字符型与整型之间可不用转换,采用默认方式。
(2)字符型与浮点型之间可不用转换,采用默认方式。
(3)日期型与字符型之间可用TO_CHAR(),TO_DATE()。
10、PROC中的4种动态SQL简介
(1)动态SQL1: 不能是查询(SELECT)语句,并且没有宿主变量.
用法:拼一串动态SQL语句,并用EXECUTE IMMEDIATE执行,如:
EXEC SQL EXECUTE IMMEDIATE "CREATE TABLE dyn1 (col1 VARCHAR2(4))";
(2)动态SQL2: 不能是查询(SELECT)语句,并且输入的宿主变量数目是知道的,
用法:拼一串动态SQL语句,用PREPARE,EXECUTE语句执行.
strcpy(c_sql, "DELETE FROM EMP WHERE EMPNO = :?");
EXEC SQL PREPARE sql_stmt FROM :c_sql;
EXEC SQL EXECUTE sql_stmt USING :emp_number;
(3)动态SQL3: 用于创建动态查询, 并且要查询的字段及输入的宿主变量数目是知道的
用法: 拼一串动态SQL语句,用PREPARE分析该语句,并要定义一个CURSOR进行取值
如:如要查询的数据按一年12月放到12张表中。表名为user_fee_1mon, user_fee_2mon,....可采用动态SQL3来进行查询
strcpy(c_sql,"select c_user_id,c_user_name,to_char(t_date,'yyyy/mm/dd hh:mi:ss'),n_fee/n");
strcat(c_sql,"from USER_FEE_");
strcat(c_sql,ac_mon);
strcat(c_sql," /n where c_user_id = :v1");
EXEC SQL PREPARE s FROM :c_sql;
EXEC SQL DECLARE cur_user_fee CURSOR FOR s;
EXEC SQL OPEN cur_user_fee USING :ac_user_id;
while(1)
{
EXEC SQL FETCH cur_user_fee into :c_user_id,:c_user_name,:c_date,:n_fee);
if (sqlca.sqlcode < 0)
{
/*FETCH CURSOR失败*/
printf("fetch cursor cur_user_fee fail,sqlcode=%ld,sqlserr=%s",sqlca.sqlcode,sqlca.sqlerrm.sqlerrmc);
}
if( sqlca.sqlcode == SQLNOTFOUND)
{
break;
}
}
EXEC SQL CLOSE cur_user_fee;
(4)动态SQL4:要处理的字段及输入的宿主变量数目和主变量的类型事先是不知道的,如:
INSERT INTO EMP () VALUES ()
是最复杂的动态SQL,很少用,在此不做介绍。
11、SQLCA:SQL是ORACLE的一个结构体,它的域用于最近的一条SQL语句执行后的一些信息,如错误号,错误描述,警告,状态等。常用的
域介绍如下:
SQLCA.sqlcode:错误号,=0正确,=1403没取到数据
SQLCA.sqlserrm.sqlerrmc:错误描述
SQLCA.sqlerrd[3]:最近的一条SQL语句所处理的行数,如果该语句处理失败,则它的值是不定的,如果错误在一个CURSOR操作中发生,则
它的值指已成功处理的行数.在DELETE,UPDATE中,它不包含因外键约束而删除,更新的那些行,
DELETE FROM EMP WHERE DEPT='SALE';
在表EMP中删除20行,但如果表EMP与表ADDRESS有外键约束,导致表ADDRESS也被删除20行,则SQLCA.sqlerrd[3]=20,而不是40。
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应注意以下几点:
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<stdio.h>
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<n; 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不能多次执行。
例如:<example.pc>
#define USERNAME “SCOTT”
#define PASSWORD “TIGER”
#include <stdio.h>
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
关键字
概念
类型
异常处理
一 概念
游标是SQL的一个内存工作区,由系统或用户以变量的形式定义。游标的作用就是用于临时存储从数据库中提取的数据块。在某些情况下,需要把数据从存放在磁盘的表中调到计算机内存中进行处理,最后将处理结果显示出来或最终写回数据库。这样数据处理的速度才会提高,否则频繁的磁盘数据交换会降低效率。
二 类型
Cursor类型包含三种: 隐式Cursor,显式Cursor和Ref Cursor(动态Cursor)。
1. 隐式Cursor:
1).对于Select …INTO…语句,一次只能从数据库中获取到一条数据,对于这种类型的DML Sql语句,就是隐式Cursor。例如:Select /Update / Insert/Delete操作。
2)作用:可以通过隐式Cusor的属性来了解操作的状态和结果,从而达到流程的控制。Cursor的属性包含:
SQL%ROWCOUNT 整型 代表DML语句成功执行的数据行数
SQL%FOUND 布尔型 值为TRUE代表插入、删除、更新或单行查询操作成功
SQL%NOTFOUND 布尔型 与SQL%FOUND属性返回值相反
SQL%ISOPEN 布尔型 DML执行过程中为真,结束后为假
3) 隐式Cursor是系统自动打开和关闭Cursor.
下面是一个Sample:
Sql代码
Set Serveroutput on;
begin
update t_contract_master set liability_state = 1 where policy_code = '123456789';
if SQL%Found then
dbms_output.put_line('the Policy is updated successfully.');
commit;
else
dbms_output.put_line('the policy is updated failed.');
end if;
end;
/
Set Serveroutput on;
begin
update t_contract_master set liability_state = 1 where policy_code = '123456789';
if SQL%Found then
dbms_output.put_line('the Policy is updated successfully.');
commit;
else
dbms_output.put_line('the policy is updated failed.');
end if;
end;
/
在PL/SQL中run:
Sql代码
SQL>
the policy is updated failed.
PL/SQL procedure successfully completed
SQL>
the policy is updated failed.
PL/SQL procedure successfully completed
2. 显式Cursor:
(1) 对于从数据库中提取多行数据,就需要使用显式Cursor。显式Cursor的属性包含:
游标的属性 返回值类型 意 义
%ROWCOUNT 整型 获得FETCH语句返回的数据行数
%FOUND 布尔型 最近的FETCH语句返回一行数据则为真,否则为假
%NOTFOUND 布尔型 与%FOUND属性返回值相反
%ISOPEN 布尔型 游标已经打开时值为真,否则为假
(2) 对于显式游标的运用分为四个步骤:
定义游标---Cursor [Cursor Name] IS;
打开游标---Open [Cursor Name];
操作数据---Fetch [Cursor name]
关闭游标---Close [Cursor Name],这个Step绝对不可以遗漏。
(3)以下是三种常见显式Cursor用法。
1)
Sql代码
Set serveroutput on;
declare
---define Cursor
Cursor cur_policy is
select cm.policy_code, cm.applicant_id, cm.period_prem,cm.bank_code,cm.bank_account
from t_contract_master cm
where cm.liability_state = 2
and cm.policy_type = 1
and cm.policy_cate in ('2','3','4')
and rownum < 5
order by cm.policy_code desc;
curPolicyInfo cur_policy%rowtype;---定义游标变量
Begin
open cur_policy; ---open cursor
Loop
--deal with extraction data from DB
Fetch cur_policy into curPolicyInfo;
Exit when cur_policy%notfound;
Dbms_Output.put_line(curPolicyInfo.policy_code);
end loop;
Exception
when others then
close cur_policy;
Dbms_Output.put_line(Sqlerrm);
if cur_policy%isopen then
--close cursor
close cur_policy;
end if;
end;
/
Set serveroutput on;
declare
---define Cursor
Cursor cur_policy is
select cm.policy_code, cm.applicant_id, cm.period_prem,cm.bank_code,cm.bank_account
from t_contract_master cm
where cm.liability_state = 2
and cm.policy_type = 1
and cm.policy_cate in ('2','3','4')
and rownum < 5
order by cm.policy_code desc;
curPolicyInfo cur_policy%rowtype;---定义游标变量
Begin
open cur_policy; ---open cursor
Loop
--deal with extraction data from DB
Fetch cur_policy into curPolicyInfo;
Exit when cur_policy%notfound;
Dbms_Output.put_line(curPolicyInfo.policy_code);
end loop;
Exception
when others then
close cur_policy;
Dbms_Output.put_line(Sqlerrm);
if cur_policy%isopen then
--close cursor
close cur_policy;
end if;
end;
/
2)
Sql代码
Set serveroutput on;
declare
Cursor cur_policy is
select cm.policy_code, cm.applicant_id, cm.period_prem,cm.bank_code,cm.bank_account
from t_contract_master cm
where cm.liability_state = 2
and cm.policy_type = 1
and cm.policy_cate in ('2','3','4')
and rownum < 5
order by cm.policy_code desc;
v_policyCode t_contract_master.policy_code%type;
v_applicantId t_contract_master.applicant_id%type;
v_periodPrem t_contract_master.period_prem%type;
v_bankCode t_contract_master.bank_code%type;
v_bankAccount t_contract_master.bank_account%type;
Begin
open cur_policy;
Loop
Fetch cur_policy into v_policyCode,
v_applicantId,
v_periodPrem,
v_bankCode,
v_bankAccount;
Exit when cur_policy%notfound;
Dbms_Output.put_line(v_policyCode);
end loop;
Exception
when others then
close cur_policy;
Dbms_Output.put_line(Sqlerrm);
if cur_policy%isopen then
close cur_policy;
end if;
end;
/
Set serveroutput on;
declare
Cursor cur_policy is
select cm.policy_code, cm.applicant_id, cm.period_prem,cm.bank_code,cm.bank_account
from t_contract_master cm
where cm.liability_state = 2
and cm.policy_type = 1
and cm.policy_cate in ('2','3','4')
and rownum < 5
order by cm.policy_code desc;
v_policyCode t_contract_master.policy_code%type;
v_applicantId t_contract_master.applicant_id%type;
v_periodPrem t_contract_master.period_prem%type;
v_bankCode t_contract_master.bank_code%type;
v_bankAccount t_contract_master.bank_account%type;
Begin
open cur_policy;
Loop
Fetch cur_policy into v_policyCode,
v_applicantId,
v_periodPrem,
v_bankCode,
v_bankAccount;
Exit when cur_policy%notfound;
Dbms_Output.put_line(v_policyCode);
end loop;
Exception
when others then
close cur_policy;
Dbms_Output.put_line(Sqlerrm);
if cur_policy%isopen then
close cur_policy;
end if;
end;
/
3)Sql代码
Set serveroutput on;
declare
Cursor cur_policy is
select cm.policy_code, cm.applicant_id, cm.period_prem,cm.bank_code,cm.bank_account
from t_contract_master cm
where cm.liability_state = 2
and cm.policy_type = 1
and cm.policy_cate in ('2','3','4')
and rownum < 5
order by cm.policy_code desc;
Begin
For rec_Policy in cur_policy loop
Dbms_Output.put_line(rec_policy.policy_code);
end loop;
Exception
when others then
Dbms_Output.put_line(Sqlerrm);
end;
/
Set serveroutput on;
declare
Cursor cur_policy is
select cm.policy_code, cm.applicant_id, cm.period_prem,cm.bank_code,cm.bank_account
from t_contract_master cm
where cm.liability_state = 2
and cm.policy_type = 1
and cm.policy_cate in ('2','3','4')
and rownum < 5
order by cm.policy_code desc;
Begin
For rec_Policy in cur_policy loop
Dbms_Output.put_line(rec_policy.policy_code);
end loop;
Exception
when others then
Dbms_Output.put_line(Sqlerrm);
end;
/
run pl/sql,执行结果如下:
Sql代码
SQL>
8780203932
8780203227
8780203218
8771289268
PL/SQL procedure successfully completed
SQL>
8780203932
8780203227
8780203218
8771289268
PL/SQL procedure successfully completed
3. Ref Cursor(动态游标):
1) 与隐式Cursor,显式Cursor的区别:Ref Cursor是可以通过在运行期间传递参数来获取数据结果集。而另外两种Cursor,是静态的,在编译期间就决定数据结果集。
2) Ref cursor的使用:
Type [Cursor type name] is ref cursor
Define 动态的Sql语句
Open cursor
操作数据---Fetch [Cursor name]
Close Cursor
下面是一个Sample:
Sql代码
Set serveroutput on;
Declare
---define cursor type name
type cur_type is ref cursor;
cur_policy cur_type;
sqlStr varchar2(500);
rec_policy t_contract_master%rowtype;
begin
---define 动态Sql
sqlStr := 'select cm.policy_code, cm.applicant_id, cm.period_prem,cm.bank_code,cm.bank_account from t_contract_master cm
where cm.liability_state = 2
and cm.policy_type = 1
and cm.policy_cate in (2,3,4)
and rownum < 5
order by cm.policy_code desc ';
---Open Cursor
open cur_policy for sqlStr;
loop
fetch cur_policy into rec_policy.policy_code, rec_policy.applicant_id, rec_policy.period_prem,rec_policy.bank_code,rec_policy.bank_account;
exit when cur_policy%notfound;
Dbms_Output.put_line('Policy_code:'||rec_policy.policy_code);
end loop;
close cur_policy;
end;
/
Set serveroutput on;
Declare
---define cursor type name
type cur_type is ref cursor;
cur_policy cur_type;
sqlStr varchar2(500);
rec_policy t_contract_master%rowtype;
begin
---define 动态Sql
sqlStr := 'select cm.policy_code, cm.applicant_id, cm.period_prem,cm.bank_code,cm.bank_account from t_contract_master cm
where cm.liability_state = 2
and cm.policy_type = 1
and cm.policy_cate in (2,3,4)
and rownum < 5
order by cm.policy_code desc ';
---Open Cursor
open cur_policy for sqlStr;
loop
fetch cur_policy into rec_policy.policy_code, rec_policy.applicant_id, rec_policy.period_prem,rec_policy.bank_code,rec_policy.bank_account;
exit when cur_policy%notfound;
Dbms_Output.put_line('Policy_code:'||rec_policy.policy_code);
end loop;
close cur_policy;
end;
/
4.常见Exception
Sql代码
1. 错 误 名 称 错误代码 错 误 含 义
2. CURSOR_ALREADY_OPEN ORA_06511 试图打开已经打开的游标
3. INVALID_CURSOR ORA_01001 试图使用没有打开的游标
4. DUP_VAL_ON_INDEX ORA_00001 保存重复值到惟一索引约束的列中
5. ZERO_DIVIDE ORA_01476 发生除数为零的除法错误
6. INVALID_NUMBER ORA_01722 试图对无效字符进行数值转换
7. ROWTYPE_MISMATCH ORA_06504 主变量和游标的类型不兼容
8. VALUE_ERROR ORA_06502 转换、截断或算术运算发生错误
9. TOO_MANY_ROWS ORA_01422 SELECT…INTO…语句返回多于一行的数据
10. NO_DATA_FOUND ORA_01403 SELECT…INTO…语句没有数据返回
11. TIMEOUT_ON_RESOURCE ORA_00051 等待资源时发生超时错误
12. TRANSACTION_BACKED_OUT ORA_00060 由于死锁,提交失败
13. STORAGE_ERROR ORA_06500 发生内存错误
14. PROGRAM_ERROR ORA_06501 发生PL/SQL内部错误
15. NOT_LOGGED_ON ORA_01012 试图操作未连接的数据库
16. LOGIN_DENIED ORA_01017 在连接时提供了无效用户名或口令