存儲過程相關資料

1 存儲過程的概念
    通常情況下,在客戶-服務器體系結構中,運行在客戶端的應用程序通過SQL對服務器端的數據庫進行訪問時,每一條SQL語句是分別地、一句一句地從客戶端向服務器發出請求,然後數據庫服務器再將結果一個一個地返回給應用程序。但是,有一些應用程序,涉及的服務器端數據庫處理活動很多,而與用戶的交互活動較少,在這種情況下,將有關數據庫的處理活動以儲存過程的形式放在數據庫服務器上完成,則可以大大減輕網上傳輸流量,提高應用程序性能。儲存過程的概念如下圖所示。
    使用儲存過程的應用程序具有以下優點:
A:減少網上傳輸流量;
B:改善服務器處理密集型應用的性能;
C:以訪問數據庫服務器特有的功能特性;
D:易於維護;
2 存儲過程的程序結構
    一個完整的使用儲存過程的應用程序由兩部分組成:一是儲存過程本身,它被存放在數據庫服務器端並數據庫服務器上運行;二是對存儲過程進行調用的客戶端應用程序,它在客戶端上運行。客戶端應用程序與服務器端儲存過程分別運行在兩個不同的進程空間中,並且有不同的功能劃分。
    客戶端應用程序的主要功能是:
    A:關數據結構和主變量定義、分配並初始化存儲空間;
    B:與數據庫進行連接;
    C:通過SQL CALL語句調用存儲過程
    D:完成事務的提交(COMMIT)和回滾(ROLLBACK)(注:除非採用分佈式工作單元,服務器端的存儲過程也可以完成事務的提交和回滾);
    E:執行CONNECT RESET語句;
    服務器端儲存過程的主要功能是:
    A:接收客戶端應用程序傳送的SQLCA和SQLDA等數據庫結構;
    B:作爲與客戶端應用程序相同的事務在數據庫服務器上運行;
    C:向客戶端應用程序返回SQLCA和其它輸出數據;
3 客戶端應用程序
    客戶端應用程序在使用CALL語句調用存儲過程之前必須先執行幾個步驟。首先,必須連接數據庫,聲明、分配並初始化SQLDA結構或宿主變量。
    提示:不要在數據庫服務器上分配這些變量結構。數據庫管理系統將根據客戶端應用程序中的結構在服務器端自動分配所需的存儲。
    客戶端應用程序必須保證在調用存儲過程之前已建立了數據庫連接,否則,將會產生錯誤。
3.1 CALL 語句
     客戶端應用程序可通過CALL語句向服務器發出存儲過程調用請求。CALL的語法格式如下所示:
     CALL {(主變量1,…,主變量N)|USING DESCRIPTION 描述符名}
     CALL語句的作用是將調用參數通過一個SQLDA數據結構從客戶端傳送給服務器端的存儲過程,存儲過程執行後再將輸出結果通過同一個SQLDA返回給客戶端應用程序。
1.有關被調用存儲過程的名稱的規定
1)在客戶端應用程序中,被調用的存儲過程的名稱既可以直接給出,也可以通過一個主變量給出。
2)如果被調用的存儲過程的名稱是直接給出的,則它必須是一個通常的標識符(identifier)並且長度不能超過254個字節。由於通常的標識符中不能含有空格或其它特殊字符,所以,如果被調用的存儲過程的名稱中必須包含有空格或其它特殊字符時,則只能使用主變量來給出其名稱。
3)如果是通過主變量給出被調用的存儲過程的名稱,則使用的主變量必須是一個具有長度屬性的字符串型的變量,並且其長度不能超過254個字節。
4)被調用的存儲過程的名稱可以採用下列幾種格式:
    proname:表示要裝入名爲proname 的存儲過程函數庫並執行其中名爲proname的函數;
    proname!funcname:表示要裝入名爲proname 的存儲過程函數庫並執行其中名爲funcname的函數;
注:按照缺省方式,在基於UNIX的平臺上,數據庫管理系統將在INSTHOME/sqllib/function目錄中查找被隔離的(fenced)存儲過程函數庫。而在Intel平臺上,存儲過程函數庫的搜索位置則由操作系統環境變量LIBPATH說明。對於不加隔離的(unfenced)存儲過程函數庫,其搜索位置分別是INSTHOME/sqllib/ unfenced(基於UNIX的系統)或sqllib/dll/unfenced(基於Intel的系統)。
    絕對路徑:如/home/user1/procname!funcname即表示要裝入/home/user1目錄下的名爲proname 的存儲過程函數庫並執行其中名爲funcname的函數;
2.通過宿主變量傳遞過程調用參數
以下是一段通過宿主變量傳遞過程調用參數的程序例子:
EXEC SQL BEGIN DECLARE SECTION;
char host_var1[15];
float host_var2;
short ind_var2;
long host_var3;
short ind_var3;
char procname[254] = “myproc”;
EXEC SQL END DECALRE SECTION;

strcopy(host_var1,”new data”);
host_var2 = 17.6;
ind_var2 = 0;
ind_var3 = -1;
EXEC SQL CALL :procname
(:host_var1,:host_var2 :ind_var2,:host_var3 :ind_var3);

    如上面的程序段所示,在調用存儲過程時,可以通過宿主變量向存儲過程傳遞調用參數。存儲過程的調用參數可分爲只輸入的(input only)、只輸出的(output only)和既輸入又輸出的(both input and output)。如果調用參數沒有明確被說明爲只輸入的(input only)或只輸出的(output only),則CALL語句將其缺省處理爲既輸入又輸出的(both input and output)的調用參數。調用參數的輸入輸出類型由相應的指示符變量確定。對於只輸入的(input only)的調用參數其相應指示符變量的值應爲0(例如,上面程序段中的host_var2);對於只輸出的(output only)的調用參數其相應指示符變量的值應爲-1( 例如,上面程序段中的host_var3);

3.通過SQLDA傳遞過程調用參數
以下是一段通過SQLDA傳遞過程調用參數的程序例子:
struct sqlda *inout_sqlda = (struct sqlda *)malloc(SQLDASIZE(3));
long host_var3;
short ind_var3 = -1;
short ind_var2 = 0;

inout_sqlda->sqln = 3;
inout_sqlda->sqld = 3;

inout_sqlda->sqlvar[0].sqltype = SQL_TYPE_CSTR;
inout_sqlda->sqlvar[0].sqllen = 16;
inout_sqlda->sqlvar[0].sqldata = (char *)malloc(inout_sqlda->sqlvar[0].sqllen);
strcpy(inout_sqlda->sqlvar[0].sqldata,”new data”);

inout_sqlda->sqlvar[1].sqltype = SQL_TYPE_NFLOAT;
inout_sqlda->sqlvar[1].sqllen = sizeof(float);
inout_sqlda->sqlvar[1].sqldata = (char *)malloc(inout_sqlda->sqlvar[1].sqllen);
*(float *) inout_sqlda->sqlvar[1].sqldata = 17.6;
inout_sqlda->sqlvar[1].sqlind = &ind_var2;

inout_sqlda->sqlvar[2].sqltype = SQL_TYPE_NINTEGER;
inout_sqlda->sqlvar[2].sqllen = sizeof(long);
inout_sqlda->sqlvar[2].sqldata = (char *)&host_var3;
inout_sqlda->sqlvar[1].sqlind = &ind_var3;

EXEC SQL CALL myproc USING DESCRIPTOR :*inout_sqlda;

如上面的程序段所示,在調用存儲過程時,也可以通過SQLDA向存儲過程傳遞調用參數。
3.2 創建存儲過程
以下是通過CREATE PROCEDURE語句創建存儲過程的例子:
CREATE PROCEDURE MYPROC(INOUT HOST1 CHAR(15),
IN HOST2 DOUBLE, OUT HOST3 INTEGER)
EXTERNAL NAME ‘/home/user1/myfn!fn1’
LANGUAGE C
PARAMETER STYLE DB2DARI

1)CREATE PROCEDURE語句的作用是向數據庫服務器註冊一個新的存儲過程;
2)MYPROC爲存儲過程的指定名。客戶端應用程序可以在CALL語句使用這一名字調用相應的存儲過程;
3)INOUT HOST1 CHAR(15)表示存儲過程中所需的一個調用參數。其中,參數輸入輸出類型INOUT表明該參數既可向存儲過程提供輸入信息,也可接收從存儲過程返回的信息。參數輸入輸出類型IN表示相應的參數只用於輸入(input only);參數輸入輸出類型OUT表示相應的參數只用於輸出(output only);
4)在同一個模式下,不允許定義名稱、參數個數和數據類型都完全相同的存儲過程;
5)EXTERNAL NAME(外部名)表示用戶爲實現所定義的存儲過程而編寫的程序代碼段的名稱;
6)LANGUAGE C 在CREATE PROCEDURE語句中是必不可少的,其作用是指明存儲過程體的語言接口約定。該子句的另一個選項是LANGUAGE JAVA;
7)PARAMETER STYLE 的作用是說明向存儲過程傳遞參數以及從存儲過程接收返回結果的有關約定。DB2DARI表明存儲過程使用的參數傳遞約定將與C語言的函數調用和連接約定相兼容;PARAMETER STYLE DB2DARI必須與LANGUAGE C一起使用。DB2GENERAL表明存儲過程使用的參數傳遞約定將與JAVA語言的函數調用和連接約定相兼容;PARAMETER STYLE DB2GENERAL必須與LANGUAGE JAVA一起使用;

4 服務器端存儲過程的實現
4.1實現存儲過程的函數定義
在UDB中,存儲過程的實現代碼一般可應用某種編程語言(如C、C++或JAVA等)來編寫。下面以C語言爲例,說明在編寫存儲過程的實現代碼時應注意的事項。
SQL_API_RC SQL_API_FN
myproc(void *reserved1,
void *reserved2,
struct sqlda * inout_sqlda,
struct sqlca *ca)
{
/* no connecttion related statements */
/* runs in background */
/* no command to terminate current process *./
/* (no exit, _exit, or at exit) */
/* if DUOW no COMMIT or ROLLBACK */
return(ret_value);
}
1)上面程序段開頭的SQL_API_RC和SQL_API_FN爲2個預定義的宏,目的是程序的可移植性;
2)在實現存儲過程的函數體中不能出現與數據庫連接相關的語句,即不能出現CONNECT,CONNECT RESET,CREATE DATABASE,DROP DATABASE,ALTER DATABASE,BACKUP,RESTORE,ROLLFORWORD等語句;
3)由於存儲過程只在後臺運行,所以不允許有寫屏幕(如,printf)的動作,但允許寫文件(fprintf);
4)存儲過程的函數體只是一個被數據庫管理系統調用的程序例程,所以當執行結束時總是應當將控制交還該調用它的函數,而不應終止當前進程,即在其函數體中不應出現exit(),_exit()這樣的函數調用;
5)如果調用存儲過程的客戶端應用程序的數據庫連接類型(CONNECT TYPE)爲DUOW,則存儲過程中不能發出終止事務的語句,即無論動態或靜態的COMMIT、ROLLBACK都不允許出現。

4.2 存儲過程的參數傳遞
SQL_API_RC SQL_API_FN
myproc(void *reserved1,void *reserved2,
struct sqlda *inout_sqlda, struct sqlca *ca)
{
struct sqlca sqlca; /*for local use */
/* use input data in SQLDA */
/* do not change SQLD,SQLTYPE, or SQLLEN */
/* do not change pointer for SQLDATA or SQLIND */
/* return data in SQLDATA(and SQLIND) */
memcpy(ca,&sqlca,sizeof(struct sqlca));


1) 存儲過程將通過SQLDA中的輸入變量得到客戶端應用程序傳來的輸入參數,然後再通過SQLDA中的輸出變量將輸出結果返回給客戶端應用程序。由於SQLDA各個域的原始值都是由客戶端應用程序在過程調用前設置的,因此,存儲過程的函數體不應改變SQLDA中的SQLD,SQLTYPE,SQLLEN等域的值。此外,雖然存儲過程的函數體可以改變SQLDATA及SQLIND中所含指針指向的變量的值,但卻不應改變SQLDATA及SQLIND中所含的指針。
注:SQLDA中的變量可以同時既是輸入變量又是輸出變量。
2) 在存儲過程的函數體返回之前,應當顯式地將本地SQLCA中的信息拷貝到存儲過程的SQLCA參數之中。

4.3存儲過程的返回值
SQL_API_RC SQL_API_FN
myproc(void *reserved1,void *reserved2,
struct sqlda *inout_sqlda, struct sqlca *ca)
{
/* processing */
/* in this application,the second SQLVAR field
is used to determine if the client intends to
call the server procedure again, A value of 0
means no further calls. */
if((*float *)inout_sqlda->sqlvar[1].sqlda != 0)
return(SQLZ_HOLD_PROC)
else
return(SQL_DISCONNECT_PROC);
}
1) 應當特別注意的是,存儲過程的返回值根本不會返回給客戶端應用程序。這裏所說的返回值的作用是使數據庫管理系統能夠確定當存儲過程執行終止時是否將存儲過程從內存中釋放掉。
2) 存儲過程可以向數據庫管理系統返回下列2個值:
—SQL_DISCONNECT_PROC:其含義是告訴數據庫管理系統當所有信息都傳遞給客戶端後,即可釋放(或卸載)存儲過程及其數據存儲;
—SQL_HOLD_PROC:其含義是告訴數據庫管理系統將存儲過程庫函數仍然保持在內存當中,這樣將可以保證當客戶端下一次發出對該存儲過程的DARI調用時被調用的庫函數已經在內存當中了,從而提高系統性能。
3) 如果客戶端只對存儲過程調用一次,則應返回SQL_DISCONNECT_PROC;
4) 如果客戶端需要對存儲過程調用多次,則應返回SQL_HOLD_PROC,從而保證存儲過程不會被卸載;
5) 如果存儲過程以SQL_HOLD_PROC作爲返回值,則當其被最後一次調用時則應以SQL_DISCONNECT_PROC作爲返回值,從而保證最後一次調用後將存儲過程從內存中釋放掉。否則該存儲過程將被一直保存在主存中,直到數據庫管理系統停止運行;
客戶端應用程序在對存儲過程進行最後一次調用時,應將這一信息通知被調用的存儲過程。
發佈了8 篇原創文章 · 獲贊 2 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章