摘要:使用 microsoft .net 框架 1.1 中的 microsoft .net oracle 提供程序訪問 oracle 存儲過程和函數(shù)。
本文討論了如何使用 ado.net 訪問 oracle 存儲過程(稱為 sql 編程塊)和函數(shù)(返回單個值的編程塊)。
您可以使用以下托管數(shù)據(jù)提供程序連接到 oracle 數(shù)據(jù)庫:microsoft .net oracle 提供程序、ole db .net 提供程序、odbc .net 數(shù)據(jù)提供程序以及 oracle 的 odp.net 提供程序。本文使用用于 oracle 的 microsoft?.net 框架數(shù)據(jù)提供程序。使用 oracle odp.net 數(shù)據(jù)提供程序或用于 ole db 的 microsoft .net 框架數(shù)據(jù)提供程序時可使用不同的功能。
oracle .net 數(shù)據(jù)提供程序隨 .net 框架 1.1 一起提供。如果您使用的是 .net 框架 1.0,您將需要下載 .net managed provider for oracle。無論是哪個版本,數(shù)據(jù)提供程序類都位于 system.data.oracleclient 命名空間中。
概述
pl/sql 是 sql 的 oracle 實現(xiàn)。它與 microsoft?sql server? 所使用的 t-sql 類似,但也有一些不同之處,本文稍后對此進(jìn)行了詳細(xì)討論。與 t-sql 一樣,pl/sql 擴(kuò)展了標(biāo)準(zhǔn) sql。pl/sql 用于定義命名編程塊,如存儲過程、函數(shù)和觸發(fā)器。
類
可使用 system.data.oracleclient 命名空間中類的子集來執(zhí)行 oracle 存儲過程和函數(shù)。下表對這些類進(jìn)行了說明:
| 類 | 說明 |
oraclecommand | 針對 oracle 數(shù)據(jù)庫執(zhí)行的存儲過程的 sql 語句。 |
oracleconnection | 打開的數(shù)據(jù)庫連接。 |
oracleparameter | oraclecommand 的參數(shù),也可能是它到 datacolumn 的映射。 |
oracleparametercollection | oracleparameter 對象的集合。 |
oracletype | oracle 數(shù)據(jù)類型和結(jié)構(gòu)的枚舉。 |
執(zhí)行存儲過程
執(zhí)行 oracle 存儲過程與執(zhí)行 sql server 存儲過程類似。下面的步驟說明了如何執(zhí)行 oracle 存儲過程和檢索它返回的結(jié)果。
1.在 hr 架構(gòu)中創(chuàng)建一個名為 count_job_history 的存儲過程,以計算 job_history 表中的記錄數(shù)。
create or new procedure count_job_history
(
reccount out number
)
is
begin
select count(*) into reccount
from job_history;
end count_job_history;
hr 架構(gòu)是默認(rèn) oracle 安裝中包含的一個示例。
2.將 system.data.oracleclient.dll(用于 oracle 的 microsoft .net 框架數(shù)據(jù)提供程序)的引用添加到項目中。
3. 使用 using 指令導(dǎo)入 oracleclient 類中的類型。
using system.data.oracleclient;
4.創(chuàng)建一個 oracleconnection 對象。
oracleconnection conn = new oracleconnection("data source=oracledb;
user id=userid;password=password;");
用您的值替換 oracle 數(shù)據(jù)庫的名稱、用戶名和密碼。
5. 創(chuàng)建一個 oraclecommand 對象。將其 connection 屬性設(shè)置為第 4 步中創(chuàng)建的連接。將其 commandtext 設(shè)置為存儲過程的名稱,并將其 commandtext 屬性設(shè)置為 commandtype.storedprocedure。當(dāng)您調(diào)用第 8 步中介紹的一個 execute() 方法時,該命令對象將執(zhí)行指定的存儲過程。
oraclecommand cmd = new oraclecommand();
cmd.connection = conn;
cmd.commandtext = "count_job_history";
cmd.commandtype = commandtype.storedprocedure;
如果您的存儲過程名稱含有特殊字符,您就必須使用轉(zhuǎn)義序列。您可以通過重置 commandtext 屬性來重用現(xiàn)有的 oraclecommand 對象。
6. 創(chuàng)建輸入、輸出和返回值的 oracleparameter 對象,并將其添加到 oraclecommand 對象的參數(shù)集合中。
cmd.parameters.add("reccount", oracletype.number).direction =
parameterdirection.output;
該行代碼是以下兩行代碼的簡寫形式:
cmd.parameters.add("reccount", oracletype.number);
cmd.parameters["reccount"].direction = parameterdirection.output;
7. 如果您要檢索結(jié)果集,請創(chuàng)建 dataset、datatable 或 datareader。在本示例中,我們只是獲取第 6 步中創(chuàng)建的輸出參數(shù)中的計數(shù)。
8. 使用 oraclecommand 對象的一個 execute 方法打開連接并執(zhí)行存儲過程,如下所示:
| 方法 | 說明 |
executereader | 通過執(zhí)行能夠返回結(jié)果集的存儲過程生成 oracledatareader。 |
executenonquery | 執(zhí)行不返回結(jié)果集的查詢或過程,返回受影響的行數(shù)。 |
executeoraclenonquery | 執(zhí)行查詢,返回受影響的行數(shù)。 該方法還使用 oraclestring 參數(shù)來返回 update、insert 或 delete 查詢所修改的最后一行的行 id。 |
executescalar | 執(zhí)行一個查詢或過程,并且返回查詢或過程的返回值,或者將結(jié)果集第一行第一列的值作為 .net 框架數(shù)據(jù)類型返回。 |
executeoraclescalar | 執(zhí)行一個查詢或過程,并且返回查詢或過程的返回值,或者將結(jié)果集第一行第一列的值作為 oracletype 數(shù)據(jù)類型返回。 |
使用完連接后,不要忘記將其關(guān)閉。
conn.open();
cmd.executenonquery();
conn.close();
如果您要使用 dataadapter 來填充 datatable 或 dataset,可以依靠 dataadapter 來打開和關(guān)閉連接。
9. 處理結(jié)果。在我們的示例中,可在顯示到控制臺的輸出參數(shù)中得到記錄數(shù):
console.writeline(cmd.parameters["reccount"].value);
下面是在本示例中開發(fā)的用于執(zhí)行存儲過程和檢索結(jié)果的代碼:
oracleconnection conn = new oracleconnection("data source=oracledb;
user id=userid;password=password;");
oraclecommand cmd = new oraclecommand();
cmd.connection = conn;
cmd.commandtext = "count_job_history";
cmd.commandtype = commandtype.storedprocedure;
cmd.parameters.add("reccount", oracletype.number).direction =
parameterdirection.output;
conn.open();
cmd.executenonquery();
conn.close();
console.writeline(cmd.parameters["reccount"].value);
不返回數(shù)據(jù)的存儲過程
oraclecommand 類的 executeoraclenonquery() 方法用于執(zhí)行不返回任何行的 sql 語句或存儲過程。該方法返回一個 int 值,表示受 update、insert 和 delete 命令影響的行數(shù);如果沒有任何行受到影響,則返回 -1。如果您所執(zhí)行的 insert、delete 或 update 語句恰好影響一行,則該方法具有單個參數(shù) oraclestring out rowid,該參數(shù)唯一標(biāo)識 oracle 數(shù)據(jù)庫中受影響的行。可以使用該值來優(yōu)化后續(xù)相關(guān)查詢。
還可以使用 oraclecommand 類的 executenonquery() 方法來執(zhí)行不返回數(shù)據(jù)的存儲過程,但您將無法獲得上面介紹的唯一行標(biāo)識符。
盡管上述命令都不會返回任何數(shù)據(jù),但映射到參數(shù)的輸出參數(shù)和返回值仍然使用數(shù)據(jù)進(jìn)行填充。這使您可以使用上述任一命令從存儲過程返回一個或多個標(biāo)量值。
以下 oracle 存儲過程刪除了由單個輸入?yún)?shù)指定的員工的所有工作經(jīng)歷,并且不返回任何數(shù)據(jù)。
create or new procedure delete_job_history
(
p_employee_id number
)
is
begin
delete from job_history
where employee_id = p_employee_id;
end delete_job_history;
以下代碼運行了該存儲過程。
// create the connection
oracleconnection conn = new oracleconnection("data source=oracledb;
user id=userid;password=password;");
// create the command for the stored procedure
oraclecommand cmd = new oraclecommand();
cmd.connection = conn;
cmd.commandtext = "count_job_history";
cmd.commandtype = commandtype.storedprocedure;
// add the parameter specifying the employee for whom to delete records
cmd.parameters.add("p_employee_id", oracletype.number).value = 102;
oraclestring rowid;
// execute the stored procedure
conn.open();
int rowsaffected = cmd.executenonquery();
conn.close();
console.writeline("rows affected: " + rowsaffected);
如果您尚未修改默認(rèn)的 hr 安裝,則 job_history 表中員工 102 的記錄被刪除,并且向控制臺輸出以下內(nèi)容:
rows affected: 1
訪問返回值
return 語句立即將控制從存儲過程返回到調(diào)用程序。oracle 存儲過程中的 return 語句無法像在 t-sql 中那樣返回值。
oracle 函數(shù)是計算并返回單個值的子程序。它們的結(jié)構(gòu)類似于存儲過程,不同之處在于它們總是具有必須返回值的 return 子句。
下面是一個返回指定員工的電子郵件的函數(shù):
create or new function get_employee_email (
p_employee_id number
)
return varchar2
is p_email varchar2(25);
begin
select email into p_email from employees
where employee_id = p_employee_id;
return p_email;
end get_employee_email;
執(zhí)行函數(shù)的方式與執(zhí)行存儲過程的方式相同。可使用 parameterdirection.returnvalue 參數(shù)獲得由函數(shù)返回的結(jié)果。以下代碼顯示了使用方法:
// create the connection
oracleconnection conn = new oracleconnection("data source=oracledb;
user id=userid;password=password;");
// create the command for the function
oraclecommand cmd = new oraclecommand();
cmd.connection = conn;
cmd.commandtext = "get_employee_email";
cmd.commandtype = commandtype.storedprocedure;
// add the parameters, including the return parameter to retrieve
// the return value
cmd.parameters.add("p_employee_id", oracletype.number).value = 101;
cmd.parameters.add("p_email", oracletype.varchar, 25).direction =
parameterdirection.returnvalue;
// execute the function
conn.open();
cmd.executenonquery();
conn.close();
// output the result
console.writeline("email address is: " + cmd.parameters["p_email"].value);
控制臺輸出顯示了員工 101 的電子郵件地址。
email address is: nkochhar
結(jié)果集與 ref cursor
可使用 ref cursor 數(shù)據(jù)類型來處理 oracle 結(jié)果集。ref cursor 是一個指向 pl/sql 查詢所返回的結(jié)果集的指針。與普通的游標(biāo)不同,ref cursor 是一個變量,它是對游標(biāo)的引用,可以在執(zhí)行時將其設(shè)置為指向不同的結(jié)果集。使用 ref cursor 輸出參數(shù)可以將 oracle 結(jié)構(gòu)化程序的結(jié)果集傳遞回調(diào)用應(yīng)用程序。通過在調(diào)用應(yīng)用程序中定義 oracletype.cursor 數(shù)據(jù)類型的輸出參數(shù),可以訪問 ref cursor 所指向的結(jié)果集。在使用 ref cursor 的過程中,oracleconnection 必須保持打開狀態(tài)。
包
pl/sql 和 t-sql 中的存儲過程之間的一個重大差異是 pl/sql 所使用的 oracle 包 結(jié)構(gòu)。在 t-sql 中沒有等效元素。包是在邏輯上相關(guān)的編程塊(如存儲過程和函數(shù))的容器。它包含兩個部分:
• 規(guī)范:定義包的名稱,并為包中的每個存儲過程或函數(shù)提供方法簽名(原型)。規(guī)范頭還定義所有全局聲明。規(guī)范的樣式類似于 c 或 c++ 頭文件。
• 正文:包含包頭中定義的存儲過程和函數(shù)的代碼。
每個存儲過程或函數(shù)的參數(shù)都出現(xiàn)在括號內(nèi),并且用逗號分隔。每個參數(shù)還根據(jù)需要用以下三個標(biāo)識符中的一個進(jìn)行標(biāo)記:
• in:該值從調(diào)用應(yīng)用程序傳遞到 pl/sql 塊。如果未指定標(biāo)識符,則 in 為默認(rèn)傳遞方向。
• out:該值由存儲過程生成,并傳遞回調(diào)用應(yīng)用程序。
• inout:該值被傳遞到 pl/sql 塊,可能在該塊內(nèi)部進(jìn)行修改,然后返回到調(diào)用應(yīng)用程序。
每個參數(shù)也都被標(biāo)記以指示數(shù)據(jù)類型。
以下包規(guī)范定義了四個過程,它們在 hr 架構(gòu)的 locations 表中創(chuàng)建、檢索、更新和刪除數(shù)據(jù)。
create or new package crud_locations as
type t_cursor is ref cursor;
procedure getlocations (cur_locations out t_cursor);
procedure updatelocations (p_location_id in number,
p_street_address in varchar2,
p_postal_code in varchar2,
p_city in varchar2,
p_state_province in varchar2,
p_country_id in char);
procedure deletelocations (p_location_id in number);
procedure insertlocations (p_location_id out number,
p_street_address in varchar2,
p_postal_code in varchar2,
p_city in varchar2,
p_state_province in varchar2,
p_country_id in char);
end crud_locations;
以下代碼摘自上述包規(guī)范的包正文,說明了 getlocations 包中的第一個過程的實現(xiàn)細(xì)節(jié):
create or new package body crud_locations as
procedure getlocations (cur_locations out t_cursor)
is
begin
open cur_locations for
select * from locations;
end getlocations;
-- implementation of other procedures ommitted.
end crud_locations;
使用 datareader
可以通過調(diào)用 oraclecommand 對象的 executereader() 方法來創(chuàng)建 oracledatareader。本節(jié)說明如何使用 datareader 來訪問由存儲過程 select_job_history 返回的結(jié)果集。以下為包規(guī)范:
create or new package select_job_history as
type t_cursor is ref cursor;
procedure getjobhistorybyemployeeid
(
p_employee_id in number,
cur_jobhistory out t_cursor
);
end select_job_history;
包正文定義了一個過程,該過程檢索指定員工的工作經(jīng)歷的結(jié)果集,并將其作為 ref cursor 輸出參數(shù)返回:
create or new package body select_job_history as
procedure getjobhistorybyemployeeid
(
p_employee_id in number,
cur_jobhistory out t_cursor
)
is
begin
open cur_jobhistory for
select * from job_history
where employee_id = p_employee_id;
end getjobhistorybyemployeeid;
end select_job_history;
以下代碼執(zhí)行該過程,根據(jù)結(jié)果集創(chuàng)建 datareader,并將 datareader 的內(nèi)容輸出到控制臺。
// create connection
oracleconnection conn = new oracleconnection("data source=oracledb;
user id=userid;password=password;");
// create the command for the stored procedure
oraclecommand cmd = new oraclecommand();
cmd.connection = conn;
cmd.commandtext = "select_job_history.getjobhistorybyemployeeid";
cmd.commandtype = commandtype.storedprocedure;
// add the parameters for the stored procedure including the ref cursor
// to retrieve the result set
cmd.parameters.add("p_employee_id", oracletype.number).value = 101;
cmd.parameters.add("cur_jobhistory", oracletype.cursor).direction =
parameterdirection.output;
// open the connection and create the datareader
conn.open();
oracledatareader dr = cmd.executereader();
// output the results and close the connection.
while(dr.read())
{
for(int i = 0; i < dr.fieldcount; i++)
console.write(dr[i].tostring() + ";");
console.writeline();
}
conn.close();
對于 hr 架構(gòu)的默認(rèn)安裝,控制臺輸出顯示了員工 101 的兩個記錄中每個記錄的字段(用分號分隔):
101;9/21/1989 12:00:00 am;10/27/1993 12:00:00 am;ac_account;110;
101;10/28/1993 12:00:00 am;3/15/1997 12:00:00 am;ac_mgr;110;
上述代碼顯示,包中的過程是使用包名稱 (elect_job_history) 和過程的名稱(在此情況下為 getjobhistorybyemployeeid)指定的,二者之間用句點分隔。
代碼還說明了如何定義結(jié)果集的 ref cursor 參數(shù)。請注意,數(shù)據(jù)類型為 oracletype.cursor,方向為 parameterdirection.output。
還請注意,在訪問 ref cursor 中的結(jié)果集的整個過程中,連接都保持打開狀態(tài)。
如果包返回多個游標(biāo),則 datareader 會按照您向參數(shù)集合中添加它們的順序來訪問這些游標(biāo),而不是按照它們在過程中出現(xiàn)的順序來訪問。可使用 datareader 的 nextresult() 方法前進(jìn)到下一個游標(biāo)。
返回單個值的存儲過程
oraclecommand 類的 executeoraclescalar() 方法用于執(zhí)行將單個值作為 oracletype 數(shù)據(jù)類型返回的 sql 語句或存儲過程。如果命令返回一個結(jié)果集,則該方法會返回第一行第一列的值。如果返回了 ref cursor,而不是返回了 ref cursor 所指向的第一行第一列的值,則該方法會返回一個空引用。oraclecommand 類的 executescalar() 方法類似于 executeoraclescalar() 方法,只不過它將值作為 .net 框架數(shù)據(jù)類型返回。
盡管如此,在使用 oracle 存儲過程時,這兩個方法都沒有用。oracle 存儲過程不能將值作為 return 語句的一部分返回,而只能將其作為 out 參數(shù)返回。有關(guān)信息,請參閱不返回數(shù)據(jù)的存儲過程一節(jié)。同時,除了通過 ref cursor 輸出參數(shù)以外,您不能返回結(jié)果集。下一節(jié)將對此進(jìn)行討論。
您只能使用 return 參數(shù)檢索 oracle 函數(shù)的返回值(如上一節(jié)所述),而不能使用 executescalar 方法之一進(jìn)行檢索。
序列
oracle 使用序列 來生成唯一編號,而不是使用 sql server 所用的數(shù)據(jù)類型 uniqueidentifier。無論是哪種情況,主要用途都是為主鍵列生成一系列唯一編號。與 uniqueidentifier 數(shù)據(jù)類型不同,序列是與將其用于主鍵值的一個或多個表無關(guān)的數(shù)據(jù)庫對象。
oracle 序列是原子對象,并且是一致的。也就是說,一旦您訪問一個序列號,oracle 將在處理下一個請求之前自動遞增下一個編號,從而確保不會出現(xiàn)重復(fù)值。
可以使用 create sequence 命令創(chuàng)建 oracle 序列。該命令所帶參數(shù)包括增量、起始值、最大值、循環(huán)和緩存。可使用 nextval 和 currval 關(guān)鍵字訪問序列值。nextval 返回序列中的下一個編號,而 currval 提供對當(dāng)前值的訪問。hr 架構(gòu)中的序列 locations_seq 按如下方式定義:
create sequence locations_seq
increment by 100
start with 1
maxvalue 9900
minvalue 1
nocycle
nocache
noorder
大多數(shù)序列代碼是不言自明的。nocycle 表示序列在達(dá)到最小值或最大值后將不再生成其他值。nocache 表示序列值在被請求之前不會進(jìn)行分配;可使用預(yù)分配機(jī)制來改善性能。noorder 表示在生成編號時,不能保證按照請求編號的順序返回這些編號。
下面的代碼顯示了一個存儲過程,該過程請求一個序列值,在向 locations 表中插入記錄時使用它設(shè)置主鍵值,然后在 out 參數(shù)中返回該主鍵值。
create or new procedure add_location (
p_location_id out number,
p_street_address in varchar2,
p_postal_code in varchar2,
p_city in varchar2,
p_state_province in varchar2,
p_country_id in char
)
as
begin
insert into locations (
location_id,
street_address,
postal_code,
city,
state_province,
country_id)
values (
locations_seq.nextval,
p_street_address,
p_postal_code,
p_city,
p_state_province,
p_country_id
);
select locations_seq.currval into p_location_id from dual;
end add_location;
下面的代碼調(diào)用該存儲過程,以插入一個記錄并檢索返回的序列值。
// create the connection
oracleconnection conn = new oracleconnection("data source=oracledb;
user id=userid;password=password;");
// create the command for the stored procedure
oraclecommand cmd = new oraclecommand();
cmd.connection = conn;
cmd.commandtext = "add_location";
cmd.commandtype = commandtype.storedprocedure;
// add the parameters for the stored procedure including the location_id
// sequence value that is returned in the output parameter p_location_id
cmd.parameters.add("p_location_id", oracletype.number).direction =
parameterdirection.output;
cmd.parameters.add("p_street_address", oracletype.varchar).value =
"123 any street";
cmd.parameters.add("p_postal_code", oracletype.varchar).value = "33040";
cmd.parameters.add("p_city", oracletype.varchar).value = "key west";
cmd.parameters.add("p_state_province", oracletype.varchar).value = "fl";
cmd.parameters.add("p_country_id", oracletype.varchar).value = "us";
// execute the command to add the records
oraclestring rowid;
conn.open();
int rowsaffected = cmd.executeoraclenonquery(out rowid);
conn.close();
// output the results
console.writeline("rows affected: " + rowsaffected);
console.writeline("location id: " +
cmd.parameters["p_location_id"].value);
控制臺顯示一個記錄被插入到該表中,同時還插入了該序列生成的主鍵值。
rows affected: 1
location id: 3300
使用 dataadapter 填充數(shù)據(jù)集
可使用 ref cursor 通過 dataadapter 來填充 dataset。下面的代碼利用了使用 datareader 一節(jié)中定義的存儲過程 getjobhistorybyemployeeid,并用它在 ref cursor 輸出參數(shù)中返回的結(jié)果集來填充 dataset。
以下是使用 dataadapter 填充 dataset 的代碼:
// create the connection
oracleconnection conn = new oracleconnection("data source=oracledb;
user id=userid;password=password;");
// create the command for the stored procedure
oraclecommand cmd = new oraclecommand();
cmd.connection = conn;
cmd.commandtext = "select_job_history.getjobhistorybyemployeeid";
cmd.commandtype = commandtype.storedprocedure;
// add the parameters for the stored procedure including the ref cursor
// to retrieve the result set
cmd.parameters.add("p_employee_id", oracletype.number).value = 101;
cmd.parameters.add("cur_jobhistory", oracletype.cursor).direction =
parameterdirection.output;
// createt the dataadapter from the command and use it to fill the
// dataset
oracledataadapter da = new oracledataadapter(cmd);
dataset ds = new dataset();
da.fill(ds);
// output the results.
console.writeline(ds.tables[0].rows.count);
對于 hr 架構(gòu)的默認(rèn)安裝,輸出表明員工 101 有兩個 job_history 記錄。
使用 dataadapter 更新 oracle
當(dāng)您使用 ref cursor 參數(shù)填充 dataset 時,不能簡單地使用 oracledataadapter 的 update() 方法。這是因為在執(zhí)行存儲過程時,oracle 不能提供確定表名和列名所需的信息。要使用 dataadapter 的 update() 方法,您必須創(chuàng)建在基礎(chǔ)表中更新、插入和刪除記錄的過程。該方法類似于在 sql server 中使用的方法。
本節(jié)說明如何生成一個可以處理所需的創(chuàng)建、檢索、更新和刪除操作的包,以便能夠從 oracle 數(shù)據(jù)庫中檢索 location 數(shù)據(jù),也能夠?qū)?dataset 數(shù)據(jù)所做的不連續(xù)更改重新更新到 oracle 數(shù)據(jù)庫。包頭如下所示:
create or new package crud_locations as
type t_cursor is ref cursor;
procedure getlocations (cur_locations out t_cursor);
procedure updatelocations (
p_location_id in number,
p_street_address in varchar2,
p_postal_code in varchar2,
p_city in varchar2,
p_state_province in varchar2,
p_country_id in char);
procedure deletelocations (p_location_id in number);
procedure insertlocations (
p_location_id out number,
p_street_address in varchar2,
p_postal_code in varchar2,
p_city in varchar2,
p_state_province in varchar2,
p_country_id in char);
end crud_locations;
包正文如下所示:
create or new package body crud_locations as
-- retrieve all location records
procedure getlocations (cur_locations out t_cursor)
is
begin
open cur_locations for
select * from locations;
end getlocations;
-- update a location record
procedure updatelocations (
p_location_id in number,
p_street_address in varchar2,
p_postal_code in varchar2,
p_city in varchar2,
p_state_province in varchar2,
p_country_id in char)
is
begin
update locations
set
street_address = p_street_address,
postal_code = p_postal_code,
city = p_city,
state_province = p_state_province,
country_id = p_country_id
where
location_id = p_location_id;
end updatelocations;
-- delete a location record
procedure deletelocations (p_location_id in number)
is
begin
delete from locations
where location_id = p_location_id;
end deletelocations;
-- insert a location record
procedure insertlocations
(
p_location_id out number,
p_street_address in varchar2,
p_postal_code in varchar2,
p_city in varchar2,
p_state_province in varchar2,
p_country_id in char
)
as
begin
insert into locations (
location_id,
street_address,
postal_code,
city,
state_province,
country_id)
values (
locations_seq.nextval,
p_street_address,
p_postal_code,
p_city,
p_state_province,
p_country_id
);
select locations_seq.currval into p_location_id from dual;
end insertlocations;
end crud_locations;
下面的代碼定義了一個 dataadapter,從而使用上述包中定義的過程來創(chuàng)建、檢索、更新和刪除支持 dataadapter 的數(shù)據(jù)。dataadapter 既可用來將數(shù)據(jù)檢索到 dataset 中,也可用來將對 dataset 所做的更改更新到 oracle 數(shù)據(jù)庫中。
// define the connection string
string connstring = "data source=oracledb;user id=userid;password=password;";
// create the data adapter
oracledataadapter da = new oracledataadapter();
// define the select command for the data adapter
oraclecommand selectcommand =
new oraclecommand("crud_locations.getlocations",
new oracleconnection(connstring));
selectcommand.commandtype = commandtype.storedprocedure;
selectcommand.parameters.add("cur_locations",
oracletype.cursor).direction = parameterdirection.output;
da.selectcommand = selectcommand;
// define the udpate command for the data adapter
oraclecommand updatecommand =
new oraclecommand("crud_locations.updatelocations",
new oracleconnection(connstring));
updatecommand.commandtype = commandtype.storedprocedure;
updatecommand.parameters.add("p_location_id", oracletype.number, 4,
"location_id");
updatecommand.parameters.add("p_street_address", oracletype.varchar, 40,
"street_address");
updatecommand.parameters.add("p_postal_code", oracletype.varchar, 12,
"postal_code");
updatecommand.parameters.add("p_city", oracletype.varchar, 30, "city");
updatecommand.parameters.add("p_state_province", oracletype.varchar, 25,
"state_province");
updatecommand.parameters.add("p_country_id", oracletype.char, 2,
"country_id");
da.updatecommand = updatecommand;
// define the delete command for the data adapter
oraclecommand deletecommand =
new oraclecommand("crud_locations.deletelocations",
new oracleconnection(connstring));
deletecommand.commandtype = commandtype.storedprocedure;
deletecommand.parameters.add("p_location_id", oracletype.number, 4,
"location_id");
da.deletecommand = deletecommand;
oraclecommand insertcommand =
new oraclecommand("crud_locations.insertlocations",
new oracleconnection(connstring));
insertcommand.commandtype = commandtype.storedprocedure;
insertcommand.parameters.add("p_location_id", oracletype.number, 4,
"location_id");
insertcommand.parameters.add("p_street_address", oracletype.varchar, 40,
"street_address");
insertcommand.parameters.add("p_postal_code", oracletype.varchar, 12,
"postal_code");
insertcommand.parameters.add("p_city", oracletype.varchar, 30, "city");
insertcommand.parameters.add("p_state_province", oracletype.varchar, 25,
"state_province");
insertcommand.parameters.add("p_country_id", oracletype.char, 2,
"country_id");
da.insertcommand = insertcommand;
// define a datatable and fill it using the data adapter
datatable dt = new datatable();
da.fill(dt);
// ... do work that adds, edits, updates, or deletes records in the table
// call the update() method of the data adapter to update the oracle
// database with changes made to the data
da.update(dt);
使用多個結(jié)果集
oracle 不支持批量查詢,因此無法從一個命令返回多個結(jié)果集。使用存儲過程時,返回多個結(jié)果集類似于返回單個結(jié)果集;必須使用 ref cursor 輸出參數(shù)。要返回多個結(jié)果集,請使用多個 ref cursor 輸出參數(shù)。
以下是返回兩個結(jié)果集(全部 employees 和 jobs 記錄)的包規(guī)范:
create or new package select_employees_jobs as
type t_cursor is ref cursor;
procedure getemployeesandjobs (
cur_employees out t_cursor,
cur_jobs out t_cursor
);
end select_employees_jobs;
包正文如下所示:
create or new package body select_employees_jobs as
procedure getemployeesandjobs
(
cur_employees out t_cursor,
cur_jobs out t_cursor
)
is
begin
-- return all employees records
open cur_employees for
select * from employees;
-- return all jobs records
open cur_jobs for
select * from jobs;
end getemployeesandjobs;
end select_employees_jobs;
以下代碼顯示了如何使用從上述包中返回的兩個結(jié)果集來填充 dataset 中的兩個相關(guān)表:
// create the connection
oracleconnection conn = new oracleconnection("data source=oracledb;
user id=userid;password=password;");
// define the command for the stored procedure
oraclecommand cmd = new oraclecommand();
cmd.connection = conn;
cmd.commandtext = "select_employees_jobs.getemployeesandjobs";
// add the parameters including the two ref cursor types to retrieve
// the two result sets
cmd.parameters.add("cur_employees", oracletype.cursor).direction =
parameterdirection.output;
cmd.parameters.add("cur_jobs", oracletype.cursor).direction =
parameterdirection.output;
cmd.commandtype = commandtype.storedprocedure;
// create the dataadapter and map tables
oracledataadapter da = new oracledataadapter(cmd);
da.tablemappings.add("table", "employees");
da.tablemappings.add("table1", "jobs");
// create and fill the dataset
dataset ds = new dataset();
da.fill(ds);
// create a relation
ds.relations.add("employees_jobs_relation",
ds.tables["jobs"].columns["job_id"],
ds.tables["employees"].columns["job_id"]);
// output the second employee (zero-based array) and job title
// based on the relation
console.writeline("employee id: " +
ds.tables["employees"].rows[1]["employee_id"] +
"; job title: " +
ds.tables["employees"].rows[1].getparentrow(
"employees_jobs_relation")["job_title"]);
控制臺輸出顯示了第二個員工的職務(wù):
employee id: 101; job title: administration vice president
小結(jié)
通過 oracle .net 數(shù)據(jù)提供程序,可以方便地執(zhí)行存儲過程以及訪問返回值(無論返回值是一個還是多個標(biāo)量值或結(jié)果集)。可以將 oracle 過程與 oracledataadapter 結(jié)合使用,從而填充 dataset、處理不連續(xù)的數(shù)據(jù)以及以后將更改更新到 oracle 數(shù)據(jù)庫。
oracle 過程與 microsoft sql server 存儲過程之間的主要區(qū)別是:oracle 過程必須將值作為輸出參數(shù)返回,并且必須使用輸出參數(shù)將結(jié)果集作為 ref cursor 對象返回給調(diào)用程序。
新聞熱點
疑難解答
圖片精選