介紹Pro*c編程的經驗

聲明:本文章轉載http://blog.csdn.net/luolunz/article/details/7490711,只爲備忘筆記使用,如有侵權通知我


PROC是ORACLE數據庫提供的編程接口之一,其應用十分的廣泛,本文通過一個具體的例子,介紹PROC編程的一些經驗及應注意的地方。

例子程序:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlda.h>
#include <sqlcpr.h>


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(" %s,%ld,%s ", 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 ",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 ",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 ",ac_text,d_demo);

(2)sscanf(ac_text, "%lf", &d_demo);
printf("ac_text=%s,d_demo=%f ",ac_text,d_demo);

(3)sscanf(ac_text, "%f", &d_demo);
printf("ac_text=%s,d_demo=%lf ",ac_text,d_demo);

(4)sscanf(ac_text, "%lf", &d_demo);
printf("ac_text=%s,d_demo=%lf ",ac_text,d_demo);

printf("******************* ");
(5)sscanf(ac_text, "%f", &f_demo);
printf("ac_text=%s,f_demo=%f ",ac_text,f_demo);

(6)sscanf(ac_text, "%lf", &f_demo);
printf("ac_text=%s,f_demo=%f ",ac_text,f_demo);

(7)sscanf(ac_text, "%f", &f_demo);
printf("ac_text=%s,f_demo=%lf ",ac_text,f_demo);

(8)sscanf(ac_text, "%lf", &f_demo);
printf("ac_text=%s,f_demo=%lf ",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 ",ac_text,d_demo);

d_demo=atof(ac_text);
printf("ac_text=%s,atof(ac_text)=%lf ",ac_text,d_demo);

f_demo=atof(ac_text);
printf("ac_text=%s,atof(ac_text)=%f ",ac_text,f_demo);

f_demo=atof(ac_text);
printf("ac_text=%s,atof(ac_text)=%lf ",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 ");
strcat(c_sql,"from USER_FEE_");
strcat(c_sql,ac_mon);
strcat(c_sql," 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 (<unknown>) VALUES (<unknown>) 
是最複雜的動態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。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Pro*C介紹-內嵌SQL 
[ Translate by Z.Jingwei. Document address:http://www-db.stanford.edu/~ullman/fcdb/oracle/or-proc.html ] 
Pro*C介紹
內嵌SQL
--------------------------------------------------------------------------------
概要 
Pro*C語法 
SQL 
預處理指令 
語句標號 
宿主變量 
基礎 
指針 
結構 
數組 
指示器變量 
數據類型同等化 
動態SQL 
事務 
錯誤處理 
SQLCA 
WHENEVER語句 
Demo程序 
C++用戶 
List of Embedded SQL Statements Supported by Pro*C
--------------------------------------------------------------------------------
概要
內嵌SQL是結合高級語言如C/C++的計算能力和SQL數據庫處理能力的一種方法。它允許你在程序中執行任意的SQL語句。Oracle的嵌入SQL環境稱爲Pro*C。
Pro*C程序分兩步編譯。首先,Pro*C的預編譯器識別出嵌入在程序中的SQL語句,並將這些語句轉換爲對SQL運行時庫(SQL runtime library)中功能(functions)的適當調用。輸出是純C/C++代碼和未被處理的純C/C++代碼。然後,用常規C/C++編譯器編譯代碼並生成可執行程序。更詳細的內容請參考Demo程序。
Demo程序。
--------------------------------------------------------------------------------
Pro*C語法
--------------------------------------------------------------------------------
SQL
所有SQL語句都要以EXEC SQL開始,並用分號";"結束。SQL語句可以放置在C/C++塊中的任何地方,但可執行的(SQL)語句應該在聲明語句後面。例:
{
    int a;
    /* ... */
    EXEC SQL SELECT salary INTO :a
             FROM Employee
             WHERE SSN=876543210;
    /* ... */
    printf("The salary is %d ", a);
    /* ... */
}

--------------------------------------------------------------------------------
預處理指令
能夠在Pro*C中正常工作的C/C++預處理指令是#include和#if。Pro*C不能識別#define。下面的代碼是錯誤的:
#define THE_SSN 876543210
/* ... */
EXEC SQL SELECT salary INTO :a
         FROM Employee
         WHERE SSN = THE_SSN;/* INVALID */
--------------------------------------------------------------------------------
語句標號
可以在SQL中跳轉到C/C++標記
    EXEC SQL WHENEVER SQLERROR GOTO error_in_SQL;
    /* ... */
error_in_SQL:
    /* do error handling */
我們會在後面的錯誤處理一節中講到有關WHENEVER的含意。
錯誤處理一節中講到有關WHENEVER的含意。
--------------------------------------------------------------------------------
宿主變量
--------------------------------------------------------------------------------
基礎
宿主變量是連接宿主程序與數據庫的關鍵。宿主變量表達式必須視爲(resolve to)左值(能被賦值)。你可以像聲明普通C變量一樣,按着C的語法規則聲明宿主變量。宿主變量的聲明可以放置在任何C變量聲明可以放置的地方。(C++用戶需要使用"DECLARE SECTION";參考C++ Users) Oracle可以使用的C數據類型包括:
C++ Users) Oracle可以使用的C數據類型包括:
char 
char[n] 
int 
short 
long 
float 
double 
VARCHAR[n] - 它是能被Pro*C的預編譯器識別的預處理類型(psuedo-type)。它用來 表示由空白填充(blank-padded,譯註:'')的變長字符串。Pro*C預編譯器會把它轉換爲有一個2字節(byte)長的域和一個n字(byte)長的字符數組的結構體。 
你不能指定寄存器存儲類型(譯註:指針)爲宿主變量。
可以在SQL表達式中使用冒號":"做前綴來引用一個宿主變量,但不能在C表達式中用分號做前綴。當使用字符串作爲宿主變量時,必須省略引用;Pro*C明白你正指定一個基於宿主變量聲明類型的字符串(譯註:這句的意思是,當定義一個字符串做爲宿主變量時char *str="string",在嵌入SQL中使用時,要省略"*"而不是*str)。不能將C函數調用和多數的指針計算表達式作爲宿主變量使用,即使它們確實被解釋爲左值。下面的代碼同時說明了合法和不合法的宿主變量的引用:
int deptnos[3] = { 000, 111, 222 };
int get_deptno() { return deptnos[2]; }
int *get_deptnoptr() { return &(deptnos[2]); }
int main() 
{
    int x; char *y; int z;
    /* ... */
    EXEC SQL INSERT INTO emp(empno, ename, deptno)
        VALUES(:x, :y, :z); /* LEGAL */
    EXEC SQL INSERT INTO emp(empno, ename, deptno)
        VALUES(:x + 1,  /* LEGAL: the reference is to x */
               'Big Shot',  /* LEGAL: but not really a host var */
               :deptnos[2]);/* LEGAL: array element is fine */
    EXEC SQL INSERT INTO emp(empno, ename, deptno)
        VALUES(:x, :y,
               :(*(deptnos+2)));/* ILLEGAL: although it has anlvalue */
    EXEC SQL INSERT INTO emp(empno, ename, deptno)
        VALUES(:x, :y,
               :get_deptno());  /* ILLEGAL: no function calls */
    EXEC SQL INSERT INTO emp(empno, ename, deptno)
        VALUES(:x, :y,
               :(*get_depnoptr())); /* ILLEGAL: although it has an lvalue */
    /* ... */
}

--------------------------------------------------------------------------------
指針
可以在SQL表達式中使用普通C語法聲明的指針。通常使用一個冒號做前綴:
int*x;
/*...*/
EXEC SQL SELECT xyz INTO :x FROM ...;
這個SELECT語句的結果會寫入*x,而不是x。

--------------------------------------------------------------------------------
結構
結構同樣可以作爲宿主變量使用,如下面例子:
typedef struct {
    char name[21];/* one greater than column length; for '' */
    int SSN;
} Emp;
/* ... */
Emp bigshot;
/* ... */
EXEC SQL INSERT INTO emp (ename, eSSN)
    VALUES (:bigshot);

--------------------------------------------------------------------------------
數組
像下面這樣使用宿主數組:
int emp_number[50];
char name[50][11];
/* ... */
EXEC SQL INSERT INTO emp(emp_number, name)
    VALUES (:emp_number, :emp_name);
這樣會一次插入所有的50個元素(tuples)。
數組只能是一維數組。例子中的char name[50][11]可能看起來與這個規則矛盾,然而,Pro*C實際上把name視爲一維字符串數組而不是二維字符數組。也可以使用結構數組。
當使用數組存放查詢結果時,如果宿主數組的大小(n)小於實際查詢後返回的結果數量時,那麼只有(查詢出來的)前n個結果被填入宿主數組。

--------------------------------------------------------------------------------
指示器變量
指示器實際上是附在宿主變量後的"NULL標記"。每一個宿主變量都可以選擇性的關聯到一個指示器變量上。指示器變量的類型必須爲2字節整形值(short類型),而且必須緊隨在宿主變量後且用冒號做爲前綴。你也可以使用關鍵字INDICATOR放在宿主變量和指示器變量之間。例如:
short indicator_var;
EXEC SQL SELECT xyz INTO :host_var:indicator_var
    FROM ...;
/* ... */
EXEC SQL INSERT INTO R
    VALUES(:host_var INDICATOR :indicator_var, ...);
在SELECT的INTO子句中使用的指示器變量可以用來檢查返回給宿主變量的值爲空(NULL)或被截斷的的情況。Oracle能夠賦給指示器變量的值具有下面各項意義:
-1 字段(譯註:原文爲column,意爲該字段的列)值爲NULL,宿主變量的值不確定。 
0 Oracle把完整的字值賦給了宿主變量。 
>0 Oracle把被截斷的字段值賦給了宿主變量。指示器變量返回的整形值是字段的原始長度。 
-2 Oracle把被截斷的字段值賦給了宿主變量,但原字段值是不確定的。(譯註:這種情況可能是數值型的值被切斷後不能確定原始值)
你也可以在INSERT或UPDATE的VALUES和SET子句中使用指示器變量指明用於輸入的宿主變量爲NULL值。你的程序可以賦給指示器變量的值具有如下各項意義:
-1 Oracle會忽略宿主變量的值並給字段賦一個NULL值。 
>=0 Oracle會將宿主變量的值賦給字段。

--------------------------------------------------------------------------------
數據類型同等化
Oracle認識兩種數據類型:內部類型和外部類型。內部數據類型指示Oracle在數據庫表中如何存儲字段。外部數據類型指示如何格式化存儲在宿主變量中用於輸入或輸出的值。在預編譯期,每一個宿主變量會被賦予一個默認的Oracle外部數據類型。數據類型同等化允許你重載默認的同等化,並允許你控制Oracle輸入數據的解釋和輸出數據的格式化。
同等化通過使用VAR表達式實現基於從變量到變量的轉換。語法是:
EXEC SQL VAR  IS  [ () ];
舉例來說, 假設你想從emp表中查詢僱員名子,然後傳給一個需要C類型(以''結尾的)字符串的程序,你不需要顯式的用''來結束這個名子。如下面所示,簡單地將宿主變量同等化爲外部數據類型STRING:
char emp_name[21];
EXEC SQL VAR emp_name IS STRING(21);
emp表中ename字段的長是20字符(character),因此,需要分配21字符(character)以適應''結束符。Oracle的外部數據類型STRING是爲C類型字符串特別設計的接口。當把ename字段值傳給emp_name時,Oracle會自動用' '結尾。
也可以使用TYPE語句將用戶自定義數據類型同等化爲Oracle外部數據類型。語法是:
EXEC SQL TYPE  IS  [ () ] [REFERENCE];
可以聲明一個用戶自定義類型的指針,顯式的如指向標量或結構的指針,或隱式的如數組,然後在TYPE表達式中使用這個類型。這種情況下,你需要在表達式後使用REFERENCE子句,如下所示:
typedef unsigned char *my_raw;
EXEC SQL TYPE my_raw IS VARRAW(4000) REFERENCE;
my_raw buffer;
/* ... */
buffer = malloc(4004);
這裏,我們分配了比源類型長(4000)更多的內存,這是因爲預編譯器要返回長度,而且可能需要填充適當的長度以滿足系統的對齊要求。

--------------------------------------------------------------------------------
動態SQL
嵌入SQL能夠滿足一個固定的應用,但有時動態產生完整的SQL語句也是很重要的。對於動態SQL,語句存儲在字符串變量中,PREPARE把字符串轉換爲SQL語句,然後用EXECUTE執行這個語句。考慮下面的例子:
char *s = "INSERT INTO emp VALUES(1234, 'jon', 3)";
EXEC SQL PREPARE q FROM :s;
EXEC SQL EXECUTE q;
PREPARE和EXECUTE可放到一個語句中,像這樣:
char *s = "INSERT INTO emp VALUES(1234, 'jon', 3)";
EXEC SQL EXECUTE IMMEDIATE :s;

--------------------------------------------------------------------------------
事務
Oracle Pro*C支持標準SQL定義的事務。一個事務是一組SQL語句集合,Oracle把它當作單獨的單元運行。一個事務從第一個SQL語句開始,遇到"EXEC SQL COMMIT"(執行當前事務對數據庫的永久修改)或"EXEC SQL ROLLBACK"(取消從事務開始到當前位置對數據庫的任何修改)時結束事務。當前事務由COMMIT或ROLLBACK語句結束後,下一條可執行SQL語句將自動開始一個新事務。
如果程序結束時沒有執行EXEC SQL COMMIT,則對數據庫的所作的修改都將被忽略。

--------------------------------------------------------------------------------
錯誤處理
在每個可執行的SQL表達式之後,可以在程序中檢查SQLCA顯式地或用WHENEVER語句隱式地得到執行狀態信息。下面將詳細地介紹這兩種方法。

--------------------------------------------------------------------------------
SQLCA
SQLCA(SQL Communications Area,SQL通訊區)用於在程序中檢查錯誤和狀態變化。每個可執行SQL語句執行後,Oracle 運行時會在這個結構中填入信息。
如果要使用SQLCA,你要用#include包含進sqlca.h頭文件。如果在很多地方包含了頭文件,你要使用#undef SQLCA來取消SQLCA的宏定義。sqlca.h中的相關程序塊如下:
#ifndef SQLCA
#define SQLCA 1
struct sqlca {
    /* ub1 */ char sqlcaid[8];
    /* b4 */ long sqlabc;
    /* b4 */ long sqlcode;
    struct {
        /* ub2 */ unsigned short sqlerrml;
        /* ub1 */ char sqlerrmc[70];
    } sqlerrm;
    /* ub1 */ char sqlerrp[8];
    /* b4 */ long sqlerrd[6];
    /* ub1 */ char sqlwarn[8];
    /* ub1 */ char sqlext[8];
};
/* ... */
sqlca域有下列的意義:
sqlcaid 該字符串域初始化爲"SQLCA",以指明這是個SQL通訊區。 
sqlcabc 該整數域用byte標識SQLCA結構的長度。 
sqlcode 這個整數域標識最近執行的SQL語句的狀態碼:
0 沒有錯誤。 
>0 語句執行但捕獲異常。當Oracle根據WHERE條件查找不到任何記錄,或SELECT INTO或FETCH影響記錄數爲0時會發生這種情況。 
<0 由於一個錯誤Oracle不能執行SQL語句。當這個錯誤發生時,多數情況下當前的事務應該回滾(rollback)。 
 
sqlerrm 這個內嵌結構包含兩個域: 
sqlerrml - 在sqlerrmc中保存的信息文本的長。 
sqlerrmc - 最大到70個字符(character)的信息文本,與sqlcode中存儲的錯誤碼相似。

sqlerrp 保留 
sqlerrd 這個二進制整形數組包含6個元素: 
sqlerrd[0] - 保留 
sqlerrd[1] - 保留 
sqlerrd[2] - 最近被執行的SQL語句影響的記錄行數。 
sqlerrd[3] - 保留 
sqlerrd[4] - 分析最近執行語句出現錯誤,錯誤的開始字符的偏移。 
sqlerrd[5] - 保留

sqlwarn 這個單字符數組包含8個元素,用於警告標誌。Oracle使用字符'W'設置一個標記。
sqlwarn[0] 當其它標記被設置時設置。 
sqlwarn[1] 當輸出宿主變量中保存的是被截斷的字段值時設置。 
sqlwarn[2] 當計算SQL統計如AVG或SUM時,如果一個NULL字段不能被使用時被設置。 
sqlwarn[3] 當SELECT的字段數與INTO句中指定的宿主變量數不等時設置。 
sqlwarn[4] 當沒有使用WHERE子句的UPDATE或DELETE語句處理了數據表中的每一行時設置。 
sqlwarn[5] 當由於PL/SQL編譯錯誤而導致procedure/function/package/package body創建命令失敗時設置。 
sqlwarn[6] 不再使用 
sqlwarn[7] 不再使用 
 
sqlext 保留
SQLCA只能在sqlerrm域中存儲最大70字符長的錯誤信息。要得到更長的(或嵌套的)全部錯誤信息字符,可以使用sqlglm()函數:
void sqlglm(char *msg_buf, size_t *buf_size, size_t *msg_length);
msg_buf是Oracle存儲錯誤信息的字符緩衝;buf_size指定msg_buf的長(byte); Oracle在*msg_length中存放實際的錯誤信息長。Oracle錯誤信息的最大長度是512字節(byte)。

--------------------------------------------------------------------------------
WHENEVER語句
這個表達式進行自動錯誤檢查和處理。語法是:
EXEC SQL WHENEVER  ;
Oracle自動檢查的SQLCA,當條件被檢測到時,程序會自動執行
可以是下列各項:
SQLWARNING - 由於Oracle返回一個警告而設置sqlwarn[0] 
SQLERROR - 由於Oracle返回一個錯誤, sqlcode的值爲負 
NOT FOUND - 由於Oracle按WHERE的條件沒有找到任何一條記錄,或SELECT INTO或FETCH返回0條記錄,而使sqlcode爲正
可以爲下列各項:
CONTINUE - 只要可能,程序會嘗試繼續運行之後的語句 
DO - 程序將控制權交給一個錯誤處理模塊 
GOTO - 程序跳轉到被標示的語句 
STOP - 調用exit()結束程序,回滾未提交的操作
下面是WHENEVER語句的例子:
EXEC SQL WHENEVER SQLWARNING DO print_warning_msg();
EXEC SQL WHENEVER NOT FOUND GOTO handle_empty;
下面是一個更詳細的例子:
/* code to find student name given id */
/* ... */
for (;;)
{
    printf("Give student id number : ");
    scanf("%d", &id);
    EXEC SQL WHENEVER NOT FOUND GOTO notfound;
    EXEC SQL SELECT studentname INTO :st_name
             FROM   student
             WHERE  studentid = :id;
    printf("Name of student is %s. ", st_name);
    continue;
notfound:
    printf("No record exists for id %d! ", id);
}
/* ... */
注意WHENEVER表達式不遵從標準C的作用域規則,整個程序都是它的作用域。舉例來說,如果下面的語句在你程序中的什麼地方(如在一個循環之前):
EXEC SQL WHENEVER NOT FOUND DO break; 
這個文件在這行之後出現的所有SQL語句都會受其影響。當不再需要WHENEVER 的來影響程序時(比如在你的循環之後),確保使用下面的語句取消它的使用。
EXEC SQL WHENEVER NOT FOUND CONTINUE;

--------------------------------------------------------------------------------
Demo程序
注意: 例程會創建、使用四個表:DEPT、EMP、PAY1和PAY2。注意你的數據庫中可能會存在相同名子的表!
在leland系統的/afs/ir/class/cs145/code/proc下有很多例程。它們被命名爲sample*.pc(C用戶)和cppdemo*.pc(C++用戶)。 ".pc"是Pro*C代碼的擴展名。由於有幾個固定的步驟複製這些文件,所以不要手工複製它們。下面介紹如何下載和設置例程:
確認你已經運行了source /afs/ir/class/cs145/all.env 
在你的目錄下,運行load_samples , 是你想放置例程的地方(例:load_samples sally etaoinshrdlu cs145_samples ) 
cd 
運行make samples(C++用戶運行make cppsamples)編譯所有的例程。
第2步將會建立樣品數據庫,創建在指定的新目錄,然後把文件複製到目錄下。它會在例程中修改你的用戶名和密碼,所有你不必每次運行例程時都要輸入用戶名和密碼。sample1 和cppdemo1也爲用戶提供了輸入用戶名和密碼的接口,當你想要學習如何使用的時候。如果在第2步輸入用戶名和密碼時產生了任何錯誤,只要在你的目錄下運行clean_sample & lt;sample_dir>,然後重做第2步到第4步。
對於第4步,你可以單獨編譯每一個例程。比如單獨編譯sample1.pc。編譯過程實際上有兩句:
proc iname=sample1.pc
把內嵌SQL代碼轉換爲相應的庫調用,輸出sample1.c 
cc sample1.c
主成可執行的sample1
編譯你自己的代碼,如foo.pc,只要修改Makefile的幾個變量:在SAMPLES變量中加入foo程序名,在SAMPLE_SRC變量中加入foo.pc源文件名。然後,寫好foo.pc 後make foo。foo.pc被預編譯爲foo.c,再編譯爲可執行的foo。C++用戶要把程序名和源文件名加入到CPPSAMPLE和CPPSAMPLE_SRC而不是SAMPLES和SAMPLE_SRC。
例程運行於下面的數據庫表上:
CREATE TABLE DEPT
    (DEPTNO    NUMBER(2) NOT NULL,
     DNAME     VARCHAR2(14),
     LOC       VARCHAR2(13));
CREATE TABLE EMP
    (EMPNO     NUMBER(4) NOT NULL,
     ENAME     VARCHAR2(10),
     JOB       VARCHAR2(9),
     MGR       NUMBER(4),
     HIREDATE  DATE,
     SAL       NUMBER(7, 2),
     COMM      NUMBER(7, 2),
     DEPTNO    NUMBER(2));
CREATE TABLE PAY1
    (ENAME     VARCHAR2(10),
     SAL       NUMBER(7, 2));
CREATE TABLE PAY2
    (ENAME     VARCHAR2(10),
     SAL       NUMBER(7, 2));
當在第2步運行load_samples的時候這些表就自動創建好了。一些tuples(譯註:沒找到合適的詞,把原詞放這了)也插入了。你可以在程序運行前查看這些表也可以任意操作這些表(如:插入、刪除或是更新tuples)。當你運行clean_sample時,這些表自動被刪除。注意:clean_sample也會清理整個;;
確保在運行這個命令之前將你自己的文件轉移到其它的地方!
你應該在運行它之前看一看源代碼,頭部的註釋描述了程序都做些什麼。例如,sample1從一個僱員的EMPNO 得到他的名子和工資,從EMP表中得到的那個僱員的佣金。
通過學習源程序你應該可以學到下面的東西:
如何從主機連接到Oracle 
如何在C/C++中嵌入SQL 
如何使用遊標 
如何使用宿主變量與數據庫通訊 
如何使用WHENEVER進行不同的錯誤處理動作 
如何使用指示器變量檢測輸出中的空值
現在,你可以使用這些技術編寫你自己的數據庫應用程序了。And have fun!

--------------------------------------------------------------------------------
C++用戶
要使用預編譯器生成合適的C++代碼,你需要注意下面的事項:
代碼被預編譯器展開。要得到C++代碼,你需要在執行proc時設置CODE=CPP選項。
解析能力。proc的PARSE選項可以是下面的值:
PARSE=NONE. C預處理指令只能在聲明節中被解析,所以所有的宿主變量需要在聲明節中聲明。
PARSE=PARTIAL. C預處理指令能被解析;然而,所有的宿主變量需要在聲明節中聲明。
PARSE=FULL. C預處理指令能被解析,而且宿主變量可以聲明在任何地方。當CODE不爲 CPP時,這是默認設置;但當CODE=CPP時,指定PARSE=FULL卻是個錯誤。
所以,C++用戶必須指定PARSE=NONE或PARSE=PARTIAL,因此這也失去了在任意地方聲明宿主變量的自由。更有,宿主變量必須被包在一個聲明節中,如下: 
EXEC SQL BEGIN DECLARE SECTION;
    // declarations...
EXEC SQL END DECLARE SECTION;
你需要使用這種方法去聲明所有的宿主和指示器變量。
文件擴展名。你要指定設置CPP_SUFFIX=cc或CPP_SUFFIX=C。
頭文件定位。默認情況下,proc像標準定位stdio.h文件一樣查找頭文件。然而C++有自己的頭文件,如iostream.h,被放置在別處。所以,你需要使用SYS_INCLUDE選項指定proc查找頭文件的路徑。
--------------------------------------------------------------------------------
Pro*C支持的嵌入SQL語句列表
聲明表達式 
EXEC SQL ARRAYLEN 在PL/SQL中使用宿主變量 
EXEC SQL BEGIN DECLARE SECTION 
EXEC SQL END DECLARE SECTION 聲明宿主變量 
EXEC SQL DECLARE 給Oracle對像命名 
EXEC SQL INCLUDE 從文件中複製 
EXEC SQL TYPE 同等化數據類型 
EXEC SQL VAR 同等化變量 
EXEC SQL WHENEVER 處理運行時錯誤 
執行表達式 
EXEC SQL ALLOCATE 聲明、控制Oracle數據 
EXEC SQL ALTER 
EXEC SQL ANALYZE 
EXEC SQL AUDIT 
EXEC SQL COMMENT 
EXEC SQL CONNECT 
EXEC SQL CREATE 
EXEC SQL DROP 
EXEC SQL GRANT 
EXEC SQL NOAUDIT 
EXEC SQL RENAME 
EXEC SQL REVOKE 
EXEC SQL TRUNCATE 
EXEC SQL CLOSE 
EXEC SQL DELETE 排序、修改Oracle數據 
EXEC SQL EXPLAIN PLAN 
EXEC SQL FETCH 
EXEC SQL INSERT 
EXEC SQL LOCK TABLE 
EXEC SQL OPEN 
EXEC SQL SELECT 
EXEC SQL UPDATE 
EXEC SQL COMMIT 處理事務 
EXEC SQL ROLLBACK 
EXEC SQL SAVEPOINT 
EXEC SQL SET TRANSACTION 
EXEC SQL DESCRIBE 使用動態SQL 
EXEC SQL EXECUTE 
EXEC SQL PREPARE 
EXEC SQL ALTER SESSION 控制會話 
EXEC SQL SET ROLE 
EXEC SQL EXECUTE 
END-EXEC 內嵌PL/SQL塊
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章