摘要 OLE DB是建立在ODBC成功基础上的一种开放规范,它为访问和操纵不同类型数据提供开放的标准。ADO是OLD DB的一个消费者,它提供了对OLE DB数据源应用级的访问功能。在应用程序中使用OLE DB和ADO,可以高效地调用返回记录集的Oracle存储过程。
关键字 OLE DB ADO 存储过程 记录集
1 前言
在基于Oracle数据库的ADO应用程序的开发过程中,为了提高执行速度和降低网络流量,往往要在应用程序中调用Oracle数据库服务器端的存储过程。有的存储过程需要返回多行的记录集。这种情况下,在应用程序中调用返回记录集的存储过程存在一定的难度。本文介绍如何使用OLE DB和ADO调用返回记录集的Oracle存储过程。
2 OLE DB和ADO的关系
2.1 OLE DB简介
OLE DB建立在ODBC成功基础上的开放的规范,它为访问和操纵不同类型数据提供开放的标准。OLE DB定义了一个COM接口集合,它封装了各种数据库管理系统服务。这些接口允许创建实现这些服务的软件组件。OLE DB组件包括数据提供者(存储和发布数据)、数据消费者(使用数据)和服务组件(处理和传输数据)。
OLE DB的设计是以消费者和提供者概念为中心。OLE DB消费者表示传统的客户方,提供者将数据以表格形式传递给消费者。OLE DB的Oracle提供者(OraOLEDB)可使OLE DB消费者高效地访问Oracle数据源。图1给出OLE DB的系统图示。OLE DB数据提供者将数据从数据源传递给消费者。在标准接口的基础上,OLE DB消费者能够访问来自提供者的数据。因为有COM组件,消费者可以用任何支持COM的编程语言访问数据。
2.2 ADO对象模在ADO对象模型中,Connection、Command和Recordset对象是三个主要的对象。Connection对象表示对远程数据的连接。Connection对象可与Command对象或Recordset对象关联。Command对象定义了对数据源执行的指定命令。Command对象可以用来执行命令和参数化的SQL语句,可以用于SQL语句和返回记录集的SQL查询。Command对象既可以使用一个活动的Connection对象,也可以创建它自己到目标数据源的连接。Command对象包含一个Parameters集合,在该集合中每一个Parameter对象表示Command对象使用的参数。Command对象执行参数化的SQL语句时,每一个Parameter对象表示SQL语句中的一个参数。Recordset对象表示的来自基本表或命令执行结果的记录全集。Recordset对象既可以使用一个活动的Connection对象,也可以创建它自己到目标数据源的连接。Recordset对象允许查询和修改数据。每一个Recordset包含一个Field对象集合,其中,每个Field对象表示Recordset中一个数据列。
2.3 ADO和OLE DB的关系
OLE DB是系统级编程接口,ADO是应用软件级编程接口。ADO是OLE DB的一个消费者,它提供了对OLE DB数据源应用级的访问功能。ADO为OLE DB提供了一个易于使用的应用级接口。ADO允许用户编写通过OLE DB提供者对数据库服务器中的数据进行访问和操作的应用程序。其主要的优点是易于使用、高速度、低内存和占用较少的磁盘空间。ADO和OLE DB的关系如图2所示:
3 Oracle中返回记录集的存储过程的定义
在Oracle存储过程中,通过定义类型为REF CURSOR(游标指针)的传出参数,可以返回记录集。OLE DB允许消费者执行带REF CURSOR类型参数的PL/SQL存储过程,或者执行返回REF CURSOR的存储函数。返回记录集的存储过程或函数必须定义在程序包里。包是Oracle数据库的一种对象,它可以将数据类型、存储过程、函数、变量和常量封装在一起。包在结构上分为包头和包体两部分,这两部分要分别建立。包头用来定义可被外部引用的元素;包体定义实际代码。使用程序包定义返回记录集的存储过程时,需要在包头中预定义自己的REF CURSOR类型。在程序包体中,存储过程或函数须使用预定义的游标类型。另外,在一个存储过程或函数中,可以定义多个REF CURSOR类型的参数以返回多个记录集。
以下以职工表emp为例,介绍如何定义返回记录集的Oracle存储过程,emp表的定义如下:
CREATE TABLE emp(
empno NUMBER(4) NOT NULL, --职工编号
ename VARCHAR2(10), --职工姓名
hiredate DATE, --雇佣日期
sal NUMBER(7,2), --工资
deptno NUMBER(2)); --所属部门编号
下面给出返回记录集的Oracle存储过程和存储函数的示例程序包头和包体,其中存储过程GetEmpRecords含有两个REF CURSOR类型的传出参数,返回两个记录集,存储函数GetDept的返回值类型为REF CURSOR,返回一个记录集。
--定义一个名为Employees的程序包头
CREATE OR REPLACE PACKAGE Employees AS
TYPE empcur IS REF CURSOR;
--empcur为自定义REF CURSOR类型
--声明一个存储过程GetEmpRecords
PROCEDURE GetEmpRecords(p_cursor OUT empcur,
q_cursor OUT empcur,
indeptno IN NUMBER,
p_errorcode OUT NUMBER);
--声明一个存储函数GetDept
FUNCTION GetDept(inempno IN NUMBER,
p_errorcode OUT NUMBER)
RETURN empcur;
END Employees; ---程序包头定义结束
--定义Employees的程序包体
--定义存储过程GetEmpRecords
CREATE OR REPLACE PACKAGE BODY Employees AS PROCEDURE GetEmpRecords(p_cursor OUT empcur, q_cursor OUT empcur,
indeptno IN NUMBER, p_errorcode OUT NUMBER) IS
BEGIN
p_errorcode := 0; -- p_errorcode为错误编号
--打开游标变量p_cursor,查询所属部门编号
--为indeptno的所有职工的信息
OPEN p_cursor FOR
SELECT * FROM emp
WHERE deptno = indeptno ORDER BY empno;
--打开游标变量q_cursor,查询所属部门编号
--为indeptno的所有职工的编号信息
OPEN q_cursor FOR
SELECT empno FROM emp
WHERE deptno = indeptno ORDER BY empno;
EXCEPTION --例外处理
WHEN OTHERS THEN
p_errorcode:= SQLCODE;
END GetEmpRecords; --存储过程GetEmpRecords定义结束
--定义存储函数GetDept
FUNCTION GetDept(inempno IN NUMBER,
p_errorcode OUT NUMBER)
RETURN empcur IS p_cursor empcur; --返回值
BEGIN
p_errorcode := 0;
--查询职工编号为inempno的职工的所属部门编号
OPEN p_cursor FOR
SELECT deptno FROM emp WHERE empno = inempno;
RETURN (p_cursor);
EXCEPTION
WHEN OTHERS THEN
p_errorcode:= SQLCODE;
END GetDept;
END Employees; --程序包体定义结束
4 利用ADO调用返回记录集的Oracle存储过程
使用OLE DB和ADO调用返回记录集的Oracle存储过程,在定义ADO对象和设置参数时,需要特定的设置。4.1 设定数据库连接字符串
在ADO应用程序中,如果要访问返回记录集的Oracle存储过程,需要使用OraOLE DB的数据库连接字符串的指定格式。在连接字符串中,PLSQLRSet属性表明OraOLE DB是否需要从PL/SQL存储过程返回记录集。如果需要返回记录集,PLSQLRSet设为1,否则设置为0,默认为 0。在Command命令执行后,该属性需要设置为0。
4.2 参数的设置
在ADO应用程序中,可以定义调用存储过程的参数。可使用Command对象的CreateParameter 方法创建Parameter 对象,并使用 Append 方法将它们添加到 Parameters 集合,在调用存储过程时,Parameters 集合将参数传递给存储过程。由于OraOLE DB采用定位传递参数的格式,即Command对象在调用存储过程时,调用参数的位置应和存储过程的参数的位置相对应。根据调用的存储过程的参数属性、名称和位置,在ADO中, Append 方法添加参数的次序应和存储过程中参数的顺序一致。
在OLE DB的标准中,没有REF CURSOR类型的预定义数据类型,因此在调用存储过程时,不能创建REF CURSOR类型的参数。在ADO调用返回记录集的存储过程时,OLE DB自动为存储过程中REF CURSOR类型的传出参数返回记录集,该记录集可以赋值给一个Recordset对象。如果PL/SQL存储过程有一个或多个REF CURSOR类型的传出参数,OLE DB将这些参数的记录集集成在一起,并通过第一个REF CURSOR类型的传出参数返回,这些记录集同样可以赋值给一个Recordset对象。
4.3 调用存储过程的格式
调用返回记录集存储过程之前,应将Command对象的 CommandType 属性指定SQL类型以优化查询性能,即CommandType 属性值设为AdCmdText。
使用 Command对象的CommandText 属性定义调用存储过程的命令。当使用Command对象执行Oracle返回记录集存储过程时,须以odbc转义符调用格式来调用存储过程,例如:{call credit_account(?,?)}。在这种格式中,传入和传出参数都用问号“?”来表示。
4.4返回记录集
可使用Command对象的 Execute 方法调用Oracle返回记录集的存储过程,并在适当的时候将结果记录集返回给 Recordset对象。该对象可以存储一个或多个结果记录集。当存储多个记录集时,可以用Recordset对象的NextRecordSet属性获得下一个记录集。
下面给出在ASP中调用返回存储过程或函数的示例程序:
<%@ Language=VBScript %>
<!--#include file="adovbs.inc"-->
<%
'Connection 对象代表了打开的、与数据源的连接。
set Con=server.CreateObject("ADODB.Connection")
set Rst1 =server.CreateObject("ADODB.Recordset")
set Rst2=server.CreateObject("ADODB.Recordset")
set Cmd =server.CreateObject("ADODB.Command")
set Prm1 =server.CreateObject("ADODB.Parameter")
set Prm2 =server.CreateObject("ADODB.Parameter")
Con.Provider = "OraOLEDB.Oracle" '设置OLE DB提供者
'设置数据库连接字符串,test是数据库网络服务名
Con.ConnectionString = "Data Source=TEST;" & _
"User ID=scott;Password=tiger;PLSQLRSet=1"
'调用返回记录集的存储过程,PLSQLRSet指定为1
Con.Open
'创建Command对象到目标数据源的连接
Cmd.ActiveConnection = Con
cmd.CommandType=adcmdtext
'虽然Employees.GetEmpRecords()有四个参数,
'但只有需要设定两个参数
'因为REF CURSOR参数由OLE DB连接提供者自动设定。
Set Prm1 = Cmd.CreateParameter("Prm1", adSmallInt, adParamInput, , 30) '创建一个输入参数,对应于indeptno
Cmd.Parameters.Append Prm1 '将参数添加到参数集合中
Set Prm2 = Cmd.CreateParameter("Prm2", adSmallInt, adParamOutput) '创建一个输出参数,对应于p_errorcode
Cmd.Parameters.Append Prm2
'调用返回记录集的存储过程必须使用odbc的转义符调用格式。
Cmd.CommandText = "{CALL Employees.GetEmpRecords(?, ?)}"
'GetEmpRecords存储过程返回两个记录集,获得第一个记录集
Set Rst1 = Cmd.Execute %>
<TABLE border = 1>
<%
Do while (Not rst1.eof) %>
<tr>
<% For Index=0 to (rst1.fields.count-1) %>
<TD VAlign=top><% = rst1(Index)%></TD>
<% Next %>
</tr>
<% rst1.MoveNext
Loop '显示第一个记录集
%>
</TABLE>
<%'获得GetEmpRecords存储过程返回的第二个记录集
Set Rst2 = Rst1.NextRecordset
'在此可加入显示第二个记录集的代码
Prm1.Value = 7369 '该参数对应于存储函数的inempno
Prm2.Value = 0 '该参数对应于存储函数的p_errorcode
'调用返回记录集的存储函数
Cmd.CommandText = "{CALL Employees.GetDept(?, ?)}"
'获得GetDept存储函数返回的记录集
Set Rst2 = Cmd.Execute
Rst1.Close
Rst2.Close
set rst1=nothing
set rst2=nothing
con.close
set con=nothing
%>
<P> </P>
</BODY>
</HTML>
5 小结
在应用程序中,使用OLE DB和ADO访问返回记录集的存储过程,OLE DB服务者可以高效地将数据库端的多行记录集返回给ADO应用程序。在ADO应用程序中,使用Command对象可以传递输入或输出参数给存储过程,并调用存储过程以获得多行记录集。
参考文献
1.(美)William G.Page, Jr.等著,王磊等译,《Oracle 8/8i开发使用手册》,机械工业出版社,北京,2000年3月,451-456页。
2.(美)Carol McCullough-Dieter著,蔡铁岭等译,《Oracle 8开发人员指南》,中国水利水电出版社,北京,2000年1月,271-276页。
3.刘阶萍等著,《深谈SQL SERVER 7.0与电子商务开发应用》,机械工业出版社,北京,2000年7月,350-352页。
4.夏毅,《ASP脚本如何调用Oracle存储过程》,《软件世界》2002年2月,68-71页