65.9K
CodeProject 正在变化。 阅读更多。
Home

如何使用 C++ 中的 ADO 调用返回一个或多个 REF CURSOR 的 Oracle 存储过程

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (17投票s)

2006 年 5 月 16 日

CPOL

2分钟阅读

viewsIcon

359636

如何使用 C++ 中的 ADO 调用返回一个或多个 REF CURSOR 的 Oracle 存储过程? 认为在互联网上很容易找到或者靠一点运气就能让它工作吗? 在发表这篇文章之前不是!

引言

本文正如标题所示 - 如何使用 C++ 中的 ADO 调用返回一个或多个 REF CURSOR 的 Oracle 存储过程。 我们在一个项目中需要用到它,由于我们对 Oracle 的了解不如对 SQL Server 的了解,因此花了几天时间在网上搜索帮助。 没有文章给出我们需要的技术。 最接近的是一篇描述如何从 VB 实现的文章。 有很多关于 Oracle/C++ 组合的库 - 但它们都没有使用 ADO 对象。 它们使用 OCI 库,这不是我们所需要的。 我们需要简单明了的 ADO - 使用智能指针 _ConnectionPtr_CommandPtr 等。我的同事Rabindra Mohapatra完成了所有的艰苦工作,非常感谢他的坚持研究,希望这篇文章最终能帮助未来和我们一样处境的人。

本文是一篇“技术”文章。 因此没有下载,没有图片。 简短,简单。

示例存储过程

CREATE OR REPLACE
PROCEDURE GetEmpRS1 (p_recordset1 OUT SYS_REFCURSOR, 
              p_recordset2 OUT SYS_REFCURSOR,
              PARAM IN STRING) AS
BEGIN
  OPEN p_recordset1 FOR
  SELECT RET1 
    FROM MYTABLE
    WHERE LOOKUPVALUE > PARAM;

  OPEN p_recordset2 FOR
  SELECT RET2
   FROM MYTABLE
   WHERE LOOKUPVALUE >= PARAM;
END GetEmpRS1;

这个存储过程接受一个输入参数用于查找,并且有两个 OUT REF CURSOR。 为了简化这个例子,两个 REF CURSOR 都返回一个列(单列)。 当然,这在现实生活中不是必需的,您也可以将 REF CURSOR 与如下的 SELECT 语句关联:SELECT * ...

什么是 REF CURSOR

如您所知,游标有助于返回记录集/结果集。 游标可能还有另一个更技术上正确的定义,但以我对数据库的有限了解,该语句听起来是正确的。 SQL Server 存储过程可以使用简单的 SELECT 语句返回“一个结果集”。 它甚至可以使用多个 SELECT 语句返回多个记录集。 Oracle 可以做到吗? 单个记录集,当然可以。 多个记录集 - 你需要所谓的 REF CURSOR。 就像一种数据类型一样,您的存储过程将 REF CURSOR 作为 OUT 参数,您可以将每个 REF CURSOR 参数中的完整记录集返回给调用者。 因此,您可以根据需要包含尽可能多的 REF CURSOR 参数 - 您的存储过程将能够返回那么多记录集。 很酷,对吧?

C++ 代码

为了简洁起见,此示例未包含错误处理。

_ConnectionPtr m_pConn;
_RecordsetPtr pRecordset;
_CommandPtr pCommand; 
_ParameterPtr pParam1;

//We will use pParam1 for the sole input parameter.
//NOTE: We must not append (hence need not create)
//the REF CURSOR parameters. If your stored proc has
//normal OUT parameters that are not REF CURSORS, you need
//to create and append them too. But not the REF CURSOR ones!

//Hardcoding the value of i/p paramter in this example...

_variant_t vt;
vt.SetString("2");

m_pConn.CreateInstance (__uuidof (Connection));
pCommand.CreateInstance (__uuidof (Command));

//NOTE the "PLSQLRSet=1" part in 
//the connection string. You can either
//do that or can set the property separately using 
//pCommand->Properties->GetItem("PLSQLRSet")->Value = true;
//But beware if you are not working with ORACLE, trying to GetItem()
//a property that does not exist 
//will throw the adErrItemNotFound exception.

m_pConn->Open (
  _bstr_t ("Provider=OraOLEDB.Oracle;PLSQLRSet=1;Data Source=XXX"), 
  _bstr_t ("CP"), _bstr_t ("CP"), adModeUnknown);
pCommand->ActiveConnection = m_pConn;

pParam1 = pCommand->CreateParameter( _bstr_t ("pParam1"), 
          adSmallInt,adParamInput, sizeof(int),( VARIANT ) vt);
pCommand->Parameters->Append(pParam1);
pRecordset.CreateInstance (__uuidof (Recordset));

//NOTE: We need to specify the stored procedure name as COMMANDTEXT
//with proper ODBC escape sequence.
//If we assign COMMANDTYPE to adCmdStoredProc and COMMANDTEXT
//to stored procedure name, it will not work in this case.
//NOTE that in the escape sequence, the number '?'-s correspond to the
//number of parameters that are NOT REF CURSORS.

pCommand->CommandText = "{CALL GetEmpRS1(?)}";

//NOTE the options set for Execute. It did not work with most other
//combinations. Note that we are using a _RecordsetPtr object
//to trap the return value of Execute call. That single _RecordsetPtr
//object will contain ALL the REF CURSOR outputs as adjacent recordsets.

pRecordset = pCommand->Execute(NULL, NULL, 
             adCmdStoredProc | adCmdUnspecified );

//After this, traverse the pRecordset object to retrieve all
//the adjacent recordsets. They will be in the order of the
//REF CURSOR parameters of the stored procedure. In this example,
//there will be 2 recordsets, as there were 2 REF CURSOR OUT params.

while( pRecordset !=NULL ) )
{
    while( !pRecordset->GetadoEOF() )
    {
        //traverse through all the records of current recordset...
    }
    long lngRec = 0;
    pRecordset = pRecordset->NextRecordset((VARIANT *)lngRec);
}

//Error handling and cleanup code (like closing recordset/ connection)
//etc are not shown here.

所有注意事项和技巧都在上面的代码片段中作为运行注释进行了讨论。 所以,我不会在文章正文中重复它们。 就是这样! 使用 ADO 从 C++ 调用存储过程有其自身的陷阱,但本文并非旨在讨论所有这些陷阱。 也许,在以后的文章中会讨论! 本文专门旨在填补互联计算机数字世界中存在的巨大空白 - 互联网上没有一篇关于如何使用 ADO 和 C++ 执行 Oracle REF CURSOR 的简单文章。 再见。

© . All rights reserved.