在我前面很多篇關於框架設計和介紹的文章里面,大多數都是利用框架提供的基礎性API進行各種的操作,包括增刪改查、分頁等各種實現和其衍生的實現,而這些實現絕大多數是基於SQL的標准操作實現的,由於框架的底層是利用了微軟企業庫Enterprise Library,因此框架也是很好的支持存儲過程的各種調用,不過由於整體性和數據庫遷移方面的考慮,建議一般使用標准的SQL操作而已,這樣能夠很大程度上保證數據庫可以很平滑過渡到其他數據庫,如Access、SQLite等單機版數據庫。但是,有時候我們提供對存儲過程的支持也是十分必要的,有些業務可能就只是固定在某種特定的數據庫上跑,如SQLServer、Oracle等這些支持存儲過程的關系型數據庫,有些業務可能還真的需要存儲過程的整體性的封裝;基於這個原因,我撰寫了這篇文章,力求從較為全面的角度上闡述存儲過程的編寫、實現和演化提煉方面做一個介紹。
1、SQLServer存儲過程的編寫
雖然存儲過程一般用於處理一些復雜的邏輯關系或者報表內容,不過為了介紹方便,我們從幾個較為基礎的操作進行介紹。
我們以一個客戶表來進行對應的存儲過程來介紹,先介紹客戶表T_Customer的表定義。
它的SQLServer腳本如下所示
create table dbo.T_Customer ( ID nvarchar(50) not null, Name nvarchar(50) null, Age int null, Creator nvarchar(50) null, CreateTime datetime null, constraint PK_T_CUSTOMER primary key (ID) )
為了介紹存儲過程的編寫,我們以這個表的相關操作的存儲過程來進行介紹,存儲過程一般可以分為下面幾種情況。
1)提供執行處理,可對執行結果進行反饋
這種情況常常可以見到,如可以對插入、更新、刪除等操作進行處理,並獲得執行的結果,下面是這兩種存儲過程的代碼。
------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:插入數據到表中 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_Insert ( @ID varchar(50), @Name varchar(50) , @Age int ) AS begin tran Insert into dbo.T_Customer( ID,Name,Age ) Values( @ID,@Name,@Age ) if @@error!=0 begin rollback end else begin commit end go ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,修改表中的數據 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_UpdateByID ( @ID varchar(50), @Name varchar(50) , @Age int ) AS begin tran Update dbo.T_Customer Set Name=@Name,Age=@Age Where ID= @ID if @@error!=0 begin rollback end else begin commit end go
------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,刪除表的記錄 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_DeleteByID ( @ID varchar(50) ) AS begin tran Delete From dbo.T_Customer where ID=@ID if @@error!=0 begin rollback end else begin commit end go
2)提供執行處理,獲得一個或者多個返回性參數,並可對執行結果進行反饋。
基於上面的處理方式,我們可能還有一種情況,就是需要執行存儲過程個,並返回對應的返回參數,我們可以在程序里面利用代碼獲取這些返回參數的數值,從而用作其他用途。
因此,這種操作,如要是獲取返回性參數的情況,如下所示是判斷記錄是否存在,以及獲取客戶最大年齡的兩個存儲過程。
------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,檢查表中是否存在符合條件的記錄 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_ExistByID ( @Exist int output , @ID varchar(50) ) AS Select @Exist = Case When Exists (Select 1 From dbo.T_Customer Where ID=@ID) Then 1 Else 0 End go ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:獲取客戶最大年齡 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_MaxAge ( @MaxAge int output ) AS Select @MaxAge=Case When Max(Age) is NULL Then 0 Else Max(Age) End From dbo.T_Customer go
3)提供查詢處理,並返回實體對象
這小節后面介紹的內容,都是存儲過程的返回值,這些或者是一條記錄,或者是多條記錄的查詢結果,這個在SQLServer里面很容易實現,而在Oracle里面需要通過游標進行處理。
下面存儲過程腳本,是基於返回單條記錄的存儲過程。
------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,檢索表中的數據 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_SelectByID ( @ID varchar(50) ) AS Select * from dbo.T_Customer Where ID= @ID go
4)提供查詢處理,並返回多條記錄集合;包括實體列表集合或DataTable集合對象
對於返回多條集合的對象,在存儲過程里面體現都一樣的,我們可能在C#處理的時候,把它轉換為不同的對象即可,返回多個集合,在SQLServer里面,它們的存儲過程代碼如下所示。
------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:檢索表中所有的數據 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_SelectAll AS Select * from dbo.T_Customer go
2、Oracle存儲過程的編寫
對應客戶表T_Customer,Oracle的創建腳本如下所示。
CREATE TABLE T_CUSTOMER ( ID VARCHAR2(100), NAME VARCHAR2(50) NOT NULL , AGE INTEGER NOT NULL, CREATOR VARCHAR2(50) NULL, CREATETIME DATE DEFAULT SYSDATE, ); ALTER TABLE T_CUSTOMER ADD CONSTRAINT PK_T_CUSTOMER PRIMARY KEY (ID);
對應SQLServer的存儲過程,Oracle的存儲過程也提供了對應的版本,下面是幾種情況下的Oracle存儲過程的編寫。
1)提供執行處理,可對執行結果進行反饋
------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:插入數據到表中 ------------------------------------ Create Or Replace Procedure T_Customer_Insert ( p_ID IN T_CUSTOMER.ID%TYPE, p_Name IN T_CUSTOMER.NAME%TYPE, p_Age IN T_CUSTOMER.AGE%TYPE ) AS Begin Insert into T_CUSTOMER( ID,NAME,AGE ) Values( p_ID,p_Name,p_Age ) ; Commit; Exception When Others Then Rollback; End; / ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,修改表中的數據 ------------------------------------ Create Or Replace Procedure T_Customer_UpdateByID ( p_ID IN T_CUSTOMER.ID%TYPE, p_Name IN T_CUSTOMER.NAME%TYPE, p_Age IN T_CUSTOMER.AGE%TYPE ) AS Begin Update T_CUSTOMER Set NAME=p_Name,AGE=p_Age Where ID= p_ID ; Commit; Exception When Others Then Rollback; End; /
------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,刪除表的記錄 ------------------------------------ Create Or Replace Procedure T_Customer_DeleteByID ( p_ID IN T_CUSTOMER.ID%TYPE ) AS Begin Delete From T_CUSTOMER where ID=p_ID ; Commit; Exception When Others Then Rollback; End; /
其中上面的代碼涉及幾個地方,T_CUSTOMER.ID%TYPE是表示根據字段動態決定參數的類型,避免應硬編碼或者反復修改參數類型。
Oracle的參數一般使用p_的前綴開始,方便區分。
2)提供執行處理,獲得一個或者多個返回性參數,並可對執行結果進行反饋。
------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,檢查表中是否存在符合條件的記錄 ------------------------------------ Create Or Replace Procedure T_Customer_ExistByID ( p_Exist OUT Number , p_ID IN T_CUSTOMER.ID%TYPE ) AS Begin --V9.i以下使用的語句 Select Case When (Count(1)>0) Then 1 Else 0 End Into p_Exist From T_CUSTOMER Where ID=p_ID ; --也可以使用的語句 -- Select Decode(Count(1),0,0,1) Into p_Exist From T_CUSTOMER Where ID=p_ID ; End; / ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:獲取表用來標識字段的最大ID值,在標識ID非自增字段時可用於數據插入時調用 ------------------------------------ Create Or Replace Procedure T_Customer_MaxAge ( p_MaxAge OUT Number ) AS Begin Select Decode(Max(Age) ,NULL,0,Max(Age)) Into p_MaxAge From T_CUSTOMER; End; /
上面的代碼,都有一個輸出的參數,雖然他們執行沒有影響記錄函數,但是這個主要是通過輸出參數的值進行處理了。
3)提供查詢處理,並返回實體對象
提供查詢處理,不管返回一條記錄,還是多條記錄,在Oracle里面,一般都是通過游標進行處理的,因此我們需要先定義一個游標類型,供我們返回記錄使用的。
下面定義一個游標的包代碼如下。
------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:創建一個包,含有一個游標類型:(一個數據庫中只需聲明一次) ------------------------------------ CREATE OR REPLACE PACKAGE MyCURSOR AS TYPE cur_OUT IS REF CURSOR; End; /
然后我們就可以在各個返回記錄的存儲過程里面使用這個游標類型了。
例如在下面的存儲過程里面,返回一條指定的數據記錄,那么輸出參數里面需要有一個游標的定義參數,但是我們在C#里面使用數據訪問框架來處理數據的時候,可以忽略他它的存在,就只需要輸入p_ID參數就可以了。
------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,檢索表中的數據 ------------------------------------ Create Or Replace Procedure T_Customer_SelectByID ( cur_OUT OUT MyCURSOR.cur_OUT , p_ID IN T_CUSTOMER.ID%TYPE ) AS Begin OPEN cur_OUT FOR Select * from T_CUSTOMER Where ID = p_ID ; End; /
4)提供查詢處理,並返回多條記錄集合;包括實體列表集合或DataTable集合對象
和上面返回單條記錄一樣,需要返回多條記錄的存儲過程,也需要使用一個游標的輸出參數來獲取返回的記錄,並可以對游標進行處理。
------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:檢索表中所有的數據 ------------------------------------ Create Or Replace Procedure T_Customer_SelectAll ( cur_OUT OUT MyCURSOR.cur_OUT ) AS Begin OPEN cur_OUT FOR Select * from T_CUSTOMER; End; /
最后,我們看看SQLServer和Oracle數據庫的腳本完整情況。
SQLServer存儲過程代碼:

------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:插入數據到表中 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_Insert ( @ID varchar(50), @Name varchar(50) , @Age int ) AS begin tran Insert into dbo.T_Customer( ID,Name,Age ) Values( @ID,@Name,@Age ) if @@error!=0 begin rollback end else begin commit end go ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,修改表中的數據 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_UpdateByID ( @ID varchar(50), @Name varchar(50) , @Age int ) AS begin tran Update dbo.T_Customer Set Name=@Name,Age=@Age Where ID= @ID if @@error!=0 begin rollback end else begin commit end go ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:檢索表中所有的數據 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_SelectAll AS Select * from dbo.T_Customer go ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,檢索表中的數據 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_SelectByID ( @ID varchar(50) ) AS Select * from dbo.T_Customer Where ID= @ID go ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,檢查表中是否存在符合條件的記錄 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_ExistByID ( @Exist int output , @ID varchar(50) ) AS Select @Exist = Case When Exists (Select 1 From dbo.T_Customer Where ID=@ID) Then 1 Else 0 End go ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,刪除表的記錄 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_DeleteByID ( @ID varchar(50) ) AS begin tran Delete From dbo.T_Customer where ID=@ID if @@error!=0 begin rollback end else begin commit end go ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:獲取客戶最大年齡 ------------------------------------ CREATE PROCEDURE dbo.T_Customer_MaxAge ( @MaxAge int output ) AS Select @MaxAge=Case When Max(Age) is NULL Then 0 Else Max(Age) End From dbo.T_Customer go
Oracle存儲過程代碼:

------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:插入數據到表中 ------------------------------------ Create Or Replace Procedure T_Customer_Insert ( p_ID IN T_CUSTOMER.ID%TYPE, p_Name IN T_CUSTOMER.NAME%TYPE, p_Age IN T_CUSTOMER.AGE%TYPE ) AS Begin Insert into T_CUSTOMER( ID,NAME,AGE ) Values( p_ID,p_Name,p_Age ) ; Commit; Exception When Others Then Rollback; End; / ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,修改表中的數據 ------------------------------------ Create Or Replace Procedure T_Customer_UpdateByID ( p_ID IN T_CUSTOMER.ID%TYPE, p_Name IN T_CUSTOMER.NAME%TYPE, p_Age IN T_CUSTOMER.AGE%TYPE ) AS Begin Update T_CUSTOMER Set NAME=p_Name,AGE=p_Age Where ID= p_ID ; Commit; Exception When Others Then Rollback; End; / ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:創建一個包,含有一個游標類型:(一個數據庫中只需聲明一次) ------------------------------------ CREATE OR REPLACE PACKAGE MyCURSOR AS TYPE cur_OUT IS REF CURSOR; End; / ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:檢索表中所有的數據 ------------------------------------ Create Or Replace Procedure T_Customer_SelectAll ( cur_OUT OUT MyCURSOR.cur_OUT ) AS Begin OPEN cur_OUT FOR Select * from T_CUSTOMER; End; / ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,檢索表中的數據 ------------------------------------ Create Or Replace Procedure T_Customer_SelectByID ( cur_OUT OUT MyCURSOR.cur_OUT , p_ID IN T_CUSTOMER.ID%TYPE ) AS Begin OPEN cur_OUT FOR Select * from T_CUSTOMER Where ID = p_ID ; End; / ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,檢查表中是否存在符合條件的記錄 ------------------------------------ Create Or Replace Procedure T_Customer_ExistByID ( p_Exist OUT Number , p_ID IN T_CUSTOMER.ID%TYPE ) AS Begin --V9.i以下使用的語句 Select Case When (Count(1)>0) Then 1 Else 0 End Into p_Exist From T_CUSTOMER Where ID=p_ID ; --V8.i及以下使用的語句 -- Select Decode(Count(1),0,0,1) Into p_Exist From T_CUSTOMER Where ID=p_ID ; End; / ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,刪除表的記錄 ------------------------------------ Create Or Replace Procedure T_Customer_DeleteByID ( p_ID IN T_CUSTOMER.ID%TYPE ) AS Begin Delete From T_CUSTOMER where ID=p_ID ; Commit; Exception When Others Then Rollback; End; / ------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:獲取表用來標識字段的最大ID值,在標識ID非自增字段時可用於數據插入時調用 ------------------------------------ Create Or Replace Procedure T_Customer_MaxAge ( p_MaxAge OUT Number ) AS Begin Select Decode(Max(Age) ,NULL,0,Max(Age)) Into p_MaxAge From T_CUSTOMER; End; /
以上就是存儲過程編寫過程中的處理和對比,下一篇將繼續介紹這個主體,並針對性的介紹如何在C#底層數據訪問里面,對這些存儲過程的使用。
本系列兩篇文章,列表如下: