[转]使用ADO.NET访问Oracle存储过程
本文转自:http://www.cnblogs.com/datasky/archive/2007/11/07/952141.html
本文讨论了如何使用 ADO.NET 访问 Oracle 存储过程(称为 SQL 编程块)和函数(返回单个值的编程块)。
您可以使用以下托管数据提供程序连接到 Oracle 数据库:Microsoft .NET Oracle 提供程序、OLE DB .NET 提供程序、ODBC .NET 数据提供程序以及 Oracle 的 ODP.NET 提供程序。本文使用用于 Oracle 的 Microsoft .NET 框架数据提供程序。使用 Oracle ODP.NET 数据提供程序或用于 OLE DB 的 Microsoft .NET 框架数据提供程序时可使用不同的功能。
Oracle .NET 数据提供程序随 .NET 框架 1.1/2.0 一起提供。如果您使用的是 .NET 框架 1.0,您将需要下载 .NET Managed Provider for Oracle。无论是哪个版本,数据提供程序类都位于 System.Data.OracleClient 命名空间中。
概述
PL/SQL 是 SQL 的 Oracle 实现。它与 Microsoft?SQL Server? 所使用的 T-SQL 类似,但也有一些不同之处,本文稍后对此进行了详细讨论。与 T-SQL 一样,PL/SQL 扩展了标准 SQL。PL/SQL 用于定义命名编程块,如存储过程、函数和触发器。
类
可使用 System.Data.OracleClient 命名空间中类的子集来执行 Oracle 存储过程和函数。下表对这些类进行了说明:
类 说明
OracleCommand
针对 Oracle 数据库执行的存储过程的 SQL 语句。
OracleConnection
打开的数据库连接。
OracleParameter
OracleCommand 的参数,也可能是它到 DataColumn 的映射。
OracleParameterCollection
OracleParameter 对象的集合。
OracleType
Oracle 数据类型和结构的枚举。
执行存储过程
执行 Oracle 存储过程与执行 SQL Server 存储过程类似。下面的步骤说明了如何执行 Oracle 存储过程和检索它返回的结果。
1.
在 HR 架构中创建一个名为 COUNT_JOB_HISTORY 的存储过程,以计算 JOB_HISTORY 表中的记录数。
CREATE OR REPLACE PROCEDURE COUNT_JOB_HISTORY
(
reccount OUT NUMBER
)
IS
BEGIN
SELECT COUNT(*) INTO reccount
FROM JOB_HISTORY;
END COUNT_JOB_HISTORY;
HR 架构是默认 Oracle 安装中包含的一个示例。
2.
将 System.Data.OracleClient.dll(用于 Oracle 的 Microsoft .NET 框架数据提供程序)的引用添加到项目中。
3.
使用 using 指令导入 OracleClient 类中的类型。
using System.Data.OracleClient;
4.
创建一个 OracleConnection 对象。
OracleConnection conn = new OracleConnection("Data Source=oracledb;
User Id=UserID;Password=Password;");
用您的值替换 Oracle 数据库的名称、用户名和密码。
5.
创建一个 OracleCommand 对象。将其 Connection 属性设置为第 4 步中创建的连接。将其 CommandText 设置为存储过程的名称,并将其 CommandText 属性设置为 CommandType.StoredProcedure。当您调用第 8 步中介绍的一个 Execute() 方法时,该命令对象将执行指定的存储过程。
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "COUNT_JOB_HISTORY";
cmd.CommandType = CommandType.StoredProcedure;
如果您的存储过程名称含有特殊字符,您就必须使用转义序列。您可以通过重置 CommandText 属性来重用现有的 OracleCommand 对象。
6.
创建输入、输出和返回值的 OracleParameter 对象,并将其添加到 OracleCommand 对象的参数集合中。
cmd.Parameters.Add("reccount", OracleType.Number).Direction =
ParameterDirection.Output;
该行代码是以下两行代码的简写形式:
cmd.Parameters.Add("reccount", OracleType.Number);
cmd.Parameters["reccount"].Direction = ParameterDirection.Output;
7.
如果您要检索结果集,请创建 DataSet、DataTable 或 DataReader。在本示例中,我们只是获取第 6 步中创建的输出参数中的计数。
8.
使用 OracleCommand 对象的一个 Execute 方法打开连接并执行存储过程,如下所示:
方法 说明
ExecuteReader
通过执行能够返回结果集的存储过程生成 OracleDataReader。
ExecuteNonQuery
执行不返回结果集的查询或过程,返回受影响的行数。
ExecuteOracleNonQuery
执行查询,返回受影响的行数。
该方法还使用 OracleString 参数来返回 UPDATE、INSERT 或 DELETE 查询所修改的最后一行的行 ID。
ExecuteScalar
执行一个查询或过程,并且返回查询或过程的返回值,或者将结果集第一行第一列的值作为 .NET 框架数据类型返回。
ExecuteOracleScalar
执行一个查询或过程,并且返回查询或过程的返回值,或者将结果集第一行第一列的值作为 OracleType 数据类型返回。
使用完连接后,不要忘记将其关闭。
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
如果您要使用 DataAdapter 来填充 DataTable 或 DataSet,可以依靠 DataAdapter 来打开和关闭连接。
9.
处理结果。在我们的示例中,可在显示到控制台的输出参数中得到记录数:
Console.WriteLine(cmd.Parameters["reccount"].Value);
下面是在本示例中开发的用于执行存储过程和检索结果的代码:
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);
不返回数据的存储过程
OracleCommand 类的 ExecuteOracleNonQuery() 方法用于执行不返回任何行的 SQL 语句或存储过程。该方法返回一个 int 值,表示受 UPDATE、INSERT 和 DELETE 命令影响的行数;如果没有任何行受到影响,则返回 -1。如果您所执行的 INSERT、DELETE 或 UPDATE 语句恰好影响一行,则该方法具有单个参数 OracleString out rowid,该参数唯一标识 Oracle 数据库中受影响的行。可以使用该值来优化后续相关查询。
还可以使用 OracleCommand 类的 ExecuteNonQuery() 方法来执行不返回数据的存储过程,但您将无法获得上面介绍的唯一行标识符。
尽管上述命令都不会返回任何数据,但映射到参数的输出参数和返回值仍然使用数据进行填充。这使您可以使用上述任一命令从存储过程返回一个或多个标量值。
以下 Oracle 存储过程删除了由单个输入参数指定的员工的所有工作经历,并且不返回任何数据。
CREATE OR REPLACE 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);
如果您尚未修改默认的 HR 安装,则 JOB_HISTORY 表中员工 102 的记录被删除,并且向控制台输出以下内容:
Rows affected: 1
访问返回值
RETURN 语句立即将控制从存储过程返回到调用程序。Oracle 存储过程中的 RETURN 语句无法像在 T-SQL 中那样返回值。
Oracle 函数是计算并返回单个值的子程序。它们的结构类似于存储过程,不同之处在于它们总是具有必须返回值的 RETURN 子句。
下面是一个返回指定员工的电子邮件的函数:
CREATE OR REPLACE 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;
执行函数的方式与执行存储过程的方式相同。可使用 ParameterDirection.ReturnValue 参数获得由函数返回的结果。以下代码显示了使用方法:
// 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
结果集与 REF CURSOR
可使用 REF CURSOR 数据类型来处理 Oracle 结果集。REF CURSOR 是一个指向 PL/SQL 查询所返回的结果集的指针。与普通的游标不同,REF CURSOR 是一个变量,它是对游标的引用,可以在执行时将其设置为指向不同的结果集。使用 REF CURSOR 输出参数可以将 Oracle 结构化程序的结果集传递回调用应用程序。通过在调用应用程序中定义 OracleType.Cursor 数据类型的输出参数,可以访问 REF CURSOR 所指向的结果集。在使用 REF CURSOR 的过程中,OracleConnection 必须保持打开状态。
包
PL/SQL 和 T-SQL 中的存储过程之间的一个重大差异是 PL/SQL 所使用的 Oracle 包 结构。在 T-SQL 中没有等效元素。包是在逻辑上相关的编程块(如存储过程和函数)的容器。它包含两个部分:
•
规范:定义包的名称,并为包中的每个存储过程或函数提供方法签名(原型)。规范头还定义所有全局声明。规范的样式类似于 C 或 C++ 头文件。
•
正文:包含包头中定义的存储过程和函数的代码。
每个存储过程或函数的参数都出现在括号内,并且用逗号分隔。每个参数还根据需要用以下三个标识符中的一个进行标记:
•
IN:该值从调用应用程序传递到 PL/SQL 块。如果未指定标识符,则 IN 为默认传递方向。
•
OUT:该值由存储过程生成,并传递回调用应用程序。
•
INOUT:该值被传递到 PL/SQL 块,可能在该块内部进行修改,然后返回到调用应用程序。
每个参数也都被标记以指示数据类型。
以下包规范定义了四个过程,它们在 HR 架构的 LOCATIONS 表中创建、检索、更新和删除数据。
CREATE OR REPLACE 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;
以下代码摘自上述包规范的包正文,说明了 GetLocations 包中的第一个过程的实现细节:
CREATE OR REPLACE 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
可以通过调用 OracleCommand 对象的 ExecuteReader() 方法来创建 OracleDataReader。本节说明如何使用 DataReader 来访问由存储过程 SELECT_JOB_HISTORY 返回的结果集。以下为包规范:
CREATE OR REPLACE 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;
包正文定义了一个过程,该过程检索指定员工的工作经历的结果集,并将其作为 REF CURSOR 输出参数返回:
CREATE OR REPLACE 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;
以下代码执行该过程,根据结果集创建 DataReader,并将 DataReader 的内容输出到控制台。
// 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 架构的默认安装,控制台输出显示了员工 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)指定的,二者之间用句点分隔。
代码还说明了如何定义结果集的 REF CURSOR 参数。请注意,数据类型为 OracleType.Cursor,方向为 ParameterDirection.Output。
还请注意,在访问 REF CURSOR 中的结果集的整个过程中,连接都保持打开状态。
如果包返回多个游标,则 DataReader 会按照您向参数集合中添加它们的顺序来访问这些游标,而不是按照它们在过程中出现的顺序来访问。可使用 DataReader 的 NextResult() 方法前进到下一个游标。
返回单个值的存储过程
OracleCommand 类的 ExecuteOracleScalar() 方法用于执行将单个值作为 OracleType 数据类型返回的 SQL 语句或存储过程。如果命令返回一个结果集,则该方法会返回第一行第一列的值。如果返回了 REF CURSOR,而不是返回了 REF CURSOR 所指向的第一行第一列的值,则该方法会返回一个空引用。OracleCommand 类的 ExecuteScalar() 方法类似于 ExecuteOracleScalar() 方法,只不过它将值作为 .NET 框架数据类型返回。
尽管如此,在使用 Oracle 存储过程时,这两个方法都没有用。Oracle 存储过程不能将值作为 RETURN 语句的一部分返回,而只能将其作为 OUT 参数返回。有关信息,请参阅不返回数据的存储过程一节。同时,除了通过 REF CURSOR 输出参数以外,您不能返回结果集。
您只能使用 RETURN 参数检索 Oracle 函数的返回值(如上一节所述),而不能使用 ExecuteScalar 方法之一进行检索。
序列
Oracle 使用序列 来生成唯一编号,而不是使用 SQL Server 所用的数据类型 uniqueidentifier。无论是哪种情况,主要用途都是为主键列生成一系列唯一编号。与 uniqueidentifier 数据类型不同,序列是与将其用于主键值的一个或多个表无关的数据库对象。
Oracle 序列是原子对象,并且是一致的。也就是说,一旦您访问一个序列号,Oracle 将在处理下一个请求之前自动递增下一个编号,从而确保不会出现重复值。
可以使用 CREATE SEQUENCE 命令创建 Oracle 序列。该命令所带参数包括增量、起始值、最大值、循环和缓存。可使用 NEXTVAL 和 CURRVAL 关键字访问序列值。NEXTVAL 返回序列中的下一个编号,而 CURRVAL 提供对当前值的访问。HR 架构中的序列 LOCATIONS_SEQ 按如下方式定义:
CREATE SEQUENCE LOCATIONS_SEQ
INCREMENT BY 100
START WITH 1
MAXVALUE 9900
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER
大多数序列代码是不言自明的。NOCYCLE 表示序列在达到最小值或最大值后将不再生成其他值。NOCACHE 表示序列值在被请求之前不会进行分配;可使用预分配机制来改善性能。NOORDER 表示在生成编号时,不能保证按照请求编号的顺序返回这些编号。
下面的代码显示了一个存储过程,该过程请求一个序列值,在向 LOCATIONS 表中插入记录时使用它设置主键值,然后在 OUT 参数中返回该主键值。
CREATE OR REPLACE 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;
下面的代码调用该存储过程,以插入一个记录并检索返回的序列值。
// 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 填充数据集
可使用 REF CURSOR 通过 DataAdapter 来填充 DataSet。下面的代码利用了使用 DataReader 一节中定义的存储过程 GetJobHistoryByEmployeeId,并用它在 REF CURSOR 输出参数中返回的结果集来填充 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 架构的默认安装,输出表明员工 101 有两个 JOB_HISTORY 记录。
使用 DataAdapter 更新 Oracle
当您使用 REF CURSOR 参数填充 DataSet 时,不能简单地使用 OracleDataAdapter 的 Update() 方法。这是因为在执行存储过程时,Oracle 不能提供确定表名和列名所需的信息。要使用 DataAdapter 的 Update() 方法,您必须创建在基础表中更新、插入和删除记录的过程。该方法类似于在 SQL Server 中使用的方法。
本节说明如何生成一个可以处理所需的创建、检索、更新和删除操作的包,以便能够从 Oracle 数据库中检索 LOCATION 数据,也能够将对 DataSet 数据所做的不连续更改重新更新到 Oracle 数据库。包头如下所示:
CREATE OR REPLACE 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 REPLACE 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,从而使用上述包中定义的过程来创建、检索、更新和删除支持 DataAdapter 的数据。DataAdapter 既可用来将数据检索到 DataSet 中,也可用来将对 DataSet 所做的更改更新到 Oracle 数据库中。
// 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);
使用多个结果集
Oracle 不支持批量查询,因此无法从一个命令返回多个结果集。使用存储过程时,返回多个结果集类似于返回单个结果集;必须使用 REF CURSOR 输出参数。要返回多个结果集,请使用多个 REF CURSOR 输出参数。
以下是返回两个结果集(全部 EMPLOYEES 和 JOBS 记录)的包规范:
CREATE OR REPLACE 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 REPLACE 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;
以下代码显示了如何使用从上述包中返回的两个结果集来填充 DataSet 中的两个相关表:
// 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"]);
控制台输出显示了第二个员工的职务:
Employee ID: 101; Job Title: Administration Vice President
小结
通过 Oracle .NET 数据提供程序,可以方便地执行存储过程以及访问返回值(无论返回值是一个还是多个标量值或结果集)。可以将 Oracle 过程与 OracleDataAdapter 结合使用,从而填充 DataSet、处理不连续的数据以及以后将更改更新到 Oracle 数据库。
Oracle 过程与 Microsoft SQL Server 存储过程之间的主要区别是:Oracle 过程必须将值作为输出参数返回,并且必须使用输出参数将结果集作为 REF CURSOR 对象返回给调用程序。
[转]使用ADO.NET访问Oracle存储过程的更多相关文章
- Ado.net 访问Oracle乱码问题
之前安装Oracle - OraClient10g_home1客户端,用Ado.net访问没有问题,后来安装ODP之后,自动的又安装了Oracle - OraClient12Home1,之后就出现乱码 ...
- oracle_fdw安装及使用(无法访问oracle存储过程等对象)
通过oracle_fdw可以访问oracle中的一些表和视图,也可以进行修改,尤其是给比较复杂的系统使用非常方便. (但不能使用oracle_fdw来访问oracle的存储过程.包.函数.序列等对象) ...
- ADO.NET访问SQL Server调用存储过程带回参
1,ADO.NET访问SQL Server调用存储过程带回参 2,DatabaseDesign use northwind go --存储过程1 --插入一条商品 productname=芹菜 un ...
- Oracle03——游标、异常、存储过程、存储函数、触发器和Java代码访问Oracle对象
作者: kent鹏 转载请注明出处: http://www.cnblogs.com/xieyupeng/p/7476717.html 1.游标(光标)Cursor 在写java程序中有集合的概念,那么 ...
- ADO访问Oracle数据库,连接异常(Unknown error 0x800a0e7a)
ADO访问Oracle数据库,连接异常(Unknown error 0x800a0e7a) 代码如下:执行Open过程中出现异常,信息为Unknown error 0x800a0e7a C++ Co ...
- EF CODEFIRST WITH ORACLE 存储过程
EF CODEFIRST WITH ORACLE 解决存储过程一直没找到解决方案 所以最后也没办法还是用了最基本的解决方案 采用Oracle.ManagedDataAccess提供的ADO基础访问类 ...
- 123 c#调用oracle存储过程返回数据集 --通过oracle存储过程返回数据集
c#调用oracle存储过程返回数据集 2008-12-20 10:59:57| 分类: net|字号 订阅 CREATE OR REPLACE PACKAGE pkg_tableTypeIS ...
- Sqlserver通过链接服务器访问Oracle的那些事儿
前言: 1.不经历风雨,怎能见彩虹. 2.充分利用BaiDu.google等搜索引擎查找资料并整合分析! 3.世上无难事只怕有心人! 本文由来:笔者在研究SQLSERVER链接服务器到oracle并使 ...
- LoadRunner调用Oracle存储过程
为了测试这个存储过程,我遥了一圈去做这个事情,这里说一下我自己接受到任务和自己开始是怎么想的. 方法一: 一开始我想着可以使用C#直接去调用存储过程,然后用Loadrunner调用C#的dll去测试, ...
随机推荐
- 系统虚拟化学习笔记——PCI设备
内容摘自<系统虚拟化:原理与实现> PCI 总线架构 PCI总线是典型的树结构.把北桥中host-PCI桥看做根,总线中其他PCI-PCI桥,PCI-ISA桥(ISA总线转PCI总线桥)等 ...
- 卡特兰数 BZOJ3907 网格 NOIP2003 栈
卡特兰数 卡特兰数2 卡特兰数:主要是求排列组合问题 1:括号化矩阵连乘,问多少种方案 2:走方格,不能过对角线,问多少种方案 3:凸边型,划分成三角形 4:1到n的序列进栈,有多少种出栈方案 NOI ...
- MVC5中使用KinEditor
参考:http://www.cnblogs.com/weicong/archive/2012/03/31/2427608.html 第一步 将 KindEditor 的源文件添加到项目中,建议放到 / ...
- Linux 命令、Shell 杂货铺
我看过一篇博客,是有关随手记录一些常用的命令的.感觉对自己比较有价值,不过其他人读起来就比较费劲了,毕竟没有什么主线.各取所需吧各位~ 1.CentOS 查看和修改系统时间和时区 date #查看系统 ...
- shmget() -- 建立共享内存
#include <sys/ipc.h>#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg); ...
- kvo原理概述
kvo概述 kvo,全称Key-Value Observing,它提供了一种方法,当对象某个属性发生改变时,允许监听该属性值变化的对象可以接受到通知,然后通过kvo的方法响应一些操作. kvo实现原理 ...
- oracle表数据误删还原
首先,找到数据删除前的一个时间点. select timestamp_to_scn(to_timestamp('2013-10-12 8:30:00', 'YYYY-MM-DD HH24:MI:SS' ...
- Unity3D之AssetBundle学习:Android上运行笔记
路径统一 在Android上加载StreamingAssets文件夹下的AssetBundle文件,首先需要对加载地址进行处理,注意PC.Android和IOS的地址不一致需要针对不同的平台不同的处理 ...
- iOS开发-表视图的使用
// // ViewController.m // Simple Table // // Created by Jierism on 16/7/20. // Copyright © 2016年 Jie ...
- ServletContext1
---------------ConfigServlet.java----------- protected void doGet(HttpServletRequest request, HttpSe ...