使用PRO*C编程的一些说明和例子

来源:互联网 发布:将军岂愿见之乎 编辑:程序博客网 时间:2024/05/20 12:22

PROC是ORACLE数据库提供的编程接口之一,其应用十分的广泛,本文通过一个具体的例子,介绍PROC编程的一些经验及应注意的地方。

#include <stdio.h>#include <stdlib.h>EXEC SQL include sqlca.h;EXEC ORACLE OPTION (RELEASE_CURSOR = YES);EXEC SQL BEGIN DECLARE SECTION;char *username = "guoxin";char *password = "guoxin";char *conn_name = "10.10.10.22:1521/ipuser";EXEC SQL END DECLARE SECTION;/*错误处理函数*/void sql_error(char *msg){printf("\n%s,%ld,%s\n", msg, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);EXEC SQL ROLLBACK RELEASE;exit(1);}int main(){EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE ERROR:");EXEC SQL CONNECT :username IDENTIFIED BY :password USING :conn_name;if (sqlca.sqlcode == 0){ printf("Conn OK\n");}else{printf("Conn ERROR\n");}EXEC SQL INSERT INTO TMP_STUDENT VALUES('1', '张三', '男');printf("insert ok\n");EXEC SQL COMMIT WORK;EXEC SQL ROLLBACK WORK RELEASE;return 0;}

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 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);
  
  (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,很少用,在此不做介绍。
  
  10、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 FORSELECT 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不能多次执行。
例如:

#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 子句可以动态改变条件。


第一个Proc程序

1、环境的搭建
安装好ORACLE后,在用户.profile文件中添加
LD_LIBRARY_PATH=/usr/lib:$ORACLE_HOME/lib:$ORACLE_HOME/ctx/lib:$ORACLE_HOME/rdbms/lib
然后
export LD_LIBRARY_PATH
2、在命令行执行proc正确后,在文件/oracle_install/product/10.2.0.1/precomp/admin/pcscfg.cfg中添加如下内容:
sys_include=(/ade/aime_rdbms_9819/oracle/precomp/public,/usr/include,/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include,/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/include,/usr/lib/gcc-lib/i386-redhat-linux7/2.96/include)
ltype=short
code=cpp
cpp_suffix=cc
parse=none
SQLCHECK=SEMANTICS
注:关于code=cpp,这里有三种模式,code=cpp、code=kr_c、code=ansi_c。采用何种方式决定了与编译proc编译后的文件后缀名和函数原型的方式。
2.1 code=cpp
此时,与编译将采用C++的原型编译方式,编译后的文件以.cc结尾。
2.2 code=kr_c和code=ansi_c
此时编译器都将按照C语言的原型方式编译,编译后文件以.c结尾。两者的区别是:
当采用code=kr_c的方式编译时,原型编译为如下形式:
extern void sqlora(/*_long *, void * _**/) ;
当采用code=ansi_c的方式编译时,原型编译形式如下:
extern void sqlora(long *, void *) ;

3、编写源程序first.pc。

#include <stdio.h>EXEC SQL include sqlca.h ;EXEC SQL BEGIN DECLARE SECTION ;char *uid = "";char *username = "guoxin";char *password = "guoxin";char *conn_name = "10.10.10.22:1521/ipuser";char *db_link_name = "ipuser";EXEC SQL END DECLARE SECTION ;int main(){EXEC SQL CONNECT :uid;//or EXEC SQL CONNECT :username IDENTIFIED BY :password USING :conn_name;if (sqlca.sqlcode == 0) { printf("Conn OK;/n") ;} else {printf("Conn ERROR;/n") ;}return 0 ;}

4、编写Makefile文件。
CC=g++
first:
proc first.pc
${CC} -o first -I$(ORACLE_HOME)/precomp/public/ -L$(ORACLE_HOME)/lib/ -lclntsh first.cc
rm -rf t* first.lis first.cc
clean:
rm -rf t* first.lis first.cc
rm -rf first
值得说明的是,如果我们采用code=cpp的方式编译,CC必须使用g++编译器,否则报错;如果采用code=ansi_c/code=kr_c的方式编译,则可使用C语言的编译器,如cc、gcc等。上述给出的是采用code=cpp的方式编译的,下面给出code=ansi_c/code=kr_c的makefile文件。
CC=cc
first:
proc first.pc
${CC} -o first -I$(ORACLE_HOME)/precomp/public/ -L$(ORACLE_HOME)/lib/ -lclntsh first.c
rm -rf t* first.lis first.c
clean:
rm -rf t* first.lis first.c
rm -rf first
注意此时的first.cc都变成了first.c文件。
5、执行make命令,则输出如下结果:
proc first.pc

Pro*C/C++: Release 10.2.0.1.0 - Production on Thu Jan 15 15:23:19 2009

Copyright (c) 1982, 2005, Oracle. All rights reserved.

System default option values taken from: /oracle_install/product/10.2.0.1/precomp/admin/pcscfg.cfg

g++ -o first -I/oracle_install/product/10.2.0.1/precomp/public/ -L/oracle_install/product/10.2.0.1/lib/ -lclntsh -lecpg first.cc
rm -rf t* first.lis first.cc
表明执行成功,通过ls命令可以看到first执行程序已经成功被编译出来。
$ ls
first first.pc makefile
$
6、运行first。
$ ./first
sqlca.sqlerrm.sqlerrmc:[];
Conn OK;
$
至此,第一个proc程序结束。


注意:
oracle proc PCC-S-02015错误
错误信息:
System default option values taken from: /u01/app/oracle/product/10.1.0/precomp/admin/pcscfg.cfg

Error at line 34, column 11 in file /usr/include/stdio.h
# include
..........1
PCC-S-02015, unable to open include file
Error at line 31, column 10 in file /usr/include/bits/types.h
#include
.........1
PCC-S-02015, unable to open include file
Error at line 14, column 10 in file /usr/include/_G_config.h
#include
.........1
PCC-S-02015, unable to open include file
Error at line 48, column 10 in file /usr/include/wchar.h
#include
.........1
PCC-S-02015, unable to open include file
Error at line 48, column 10 in file /usr/include/wchar.h
#include
.........1
PCC-S-02015, unable to open include file
Error at line 31, column 10 in file /usr/include/gconv.h
#include
.........1
PCC-S-02015, unable to open include file
解决修改:$ORACLE_HOME/precomp/admin/pcscfg.cfg
(网络上很多答案是添加选项parse=none,意思是忽略头文件预编译.如果说含有宏定义则根本没有解决.不提倡)
在/usr/lib/下查找未找到的"stddef.h"文件,找到路径在pcscfg.cfg修改或添加该路径.
find . -name "stddef.h" -print

./gcc-lib/i386-redhat-linux/3.2.3/include/stddef.h
原来:sys_include=(/tmp/p/precomp/public,/usr/include,/usr/lib/gcc-lib/i486-suse-linux/3.2.2/include,/usr/lib/gcc-lib/i486-suse-linux/3.2/include,/usr/lib/gcc-lib/i386-redhat-linux/4.0.0/include,/usr/lib/gcc-lib/i386-redhat-linux7/3.2.3/include,/usr/lib/gcc-lib/i586-suse-linux/3.3.3/include)

改为:
sys_include=(/tmp/p/precomp/public,/usr/include,/usr/lib/gcc-lib/i486-suse-linux/3.2.2/include,/usr/lib/gcc-lib/i486-suse-linux/3.2/include,/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/include,/usr/lib/gcc-lib/i386-redhat-linux7/3.2.3/include,/usr/lib/gcc-lib/i586-suse-linux/3.3.3/include)

如果使用时为乱码,执行
export NLS_LANG="Simplified Chinese_china.AL32UTF8"




0 0