1. 在进入正题之前,先讲点别的,如何在VC中连接Sybase数据库,
连接字符窜为,_bstr_t strCnn(\"Driver={Sybase System 11};Srvr=RRRRR;Uid=RRR_Mao_bb1;Pwd=user2\");
这里,RRRRR是数据库的名称,已经在Sybase->sdedit中设定好了
RRR_Mao_bb1 和 user2分别是用户名和密码
不过使用这种基于ODBC的连接使用一段时间以后,就出现问题了,出现了“Catastrophic failure”的错误,微软的解释是 http://support.microsoft.com/kb/243349/en-us
2. 为了使我们的调用存储过程的例子有更多的通用性,我建了有输入参数,有输出参数,有一个返回记录集,有一个返回值的存储过程,如下:
CREATE PROCEDURE sp_1 ( @pin1 int , @pin2 CHAR(10), @pout1 int OUTPUT, @pout2 CHAR(10) OUTPUT ) AS BEGIN declare @retval int select @pout1 = @pin1 + 100 select @pout2 = left( ltrim(rtrim(@pin2)) + \'123\' , 10) select Num,Name,Date from TABLE1 select @retval = 1236 return @retval END |
对于这个SP来说,他这些个参数是
@RETURN_VALUE(int ,返回值)
@pin1 ( int ,输入 )
@pin2 ( char(10) ,输入 )
@pout1 (int ,输入/输出)
@pout1 ( char(10) , 输入/输出)
@RETURN_VALUE是第0个参数,@pin1是第1个,依此类推
以上信息可以在SQL 的查询分析器中看到,注意,这些参数的顺序很重要
3.调用的前期准备
这就不多说了,什么import 库阿,建立连接阿,什么的,不多说了。
假定连接是pConn
注意,这里要把pConn设定成adUseClient型
pConn->CursorLocation =adUseClient;
下面我要贴具体的代码了,为了精简所贴的代码,我把所有的捕获异常都没贴出来(try catch)
4.使用Refresh的方法来调用
先定义一些变量
_CommandPtr pCmd = NULL; _RecordsetPtr pRecordset = NULL; |
初试化他们
pCmd.CreateInstance(__uuidof(Command)); pRecordset.CreateInstance(_uuidof(Recordset)); pCmd->ActiveConnection = pConn; pCmd->CommandType = adCmdStoredProc; pCmd->CommandText=_bstr_t(_T(\"sp_1\")); //SP Name |
pCmd->Parameters->Refresh(); pCmd->Parameters->Item[_variant_t(_bstr_t(\"@pin2\") )]->Value=_variant_t(\"DD\"); |
这个refresh一定要有,调SP
pRecordset = pCmd->Execute(NULL,NULL,adCmdStoredProc); int retVal = -1; _variant_t VretVal ; //GetRetVal VretVal = pCmd->Parameters->GetItem(short(0))->Value; //output1 Info= (LPCTSTR)_bstr_t(VretVal); //取记录集里面的内容 if (pRecordset->adoBOF && pRecordset->adoEOF) { MessageBox(\"没有符合条件的记录存在!\",\"提示\"); if(pRecordset != NULL && pRecordset->State) { pRecordset->Close(); pRecordset = NULL; } pCmd.Detach(); return; } { VRectVal = pRecordset->GetCollect(_T(\"Name\")); StrVal = (LPCTSTR)_bstr_t(VRectVal); m_list.AddString(StrVal); } |
最后pCmd.Detach();
在这里,有一点要注意的是
VretVal = pCmd->Parameters->GetItem(short(4))->Value;
这里的4,就是哪个output参数的index,就是我在2中说的参数的顺序
这里使用了Refresh,这是一个很重要的函数,我将在下面介绍一下它,我要先贴出另一种,不使用Refresh的办法,
5 .使用非Refresh的方法来调用
先定义变量
_CommandPtr pCmd = NULL; _RecordsetPtr pRecordset = NULL; _ParameterPtr retParam = NULL; |
初试化
pCmd.CreateInstance(__uuidof(Command)); retParam.CreateInstance(__uuidof(Parameter)); //其他的ParameterPtr 也初试化 retParam = pCmd ->CreateParameter(_bstr_t(\"Return\"), adInteger, pCmd->Parameters->Append(retParam); inParam1->Value = _variant_t(3); pCmd->Parameters->Append(inParam1); inParam2 = pCmd ->CreateParameter(_bstr_t(\"InParam2\"), pCmd->Parameters->Append(inParam2); outParam1 = pCmd ->CreateParameter(_bstr_t(\"OutParam1\"), pCmd->Parameters->Append(outParam1); outParam2 = pCmd ->CreateParameter(_bstr_t(\"OutParam2\"), pCmd->Parameters->Append(outParam2); |
这里不用refresh的办法,是用Parameter来,这里要注意的是,这个add的顺序要和参数的index顺序要一致
下面就是调用了
pRecordset = pCmd->Execute(NULL,NULL,adCmdStoredProc);
然后取那些return值阿,output值阿什么的
这里有一个区别,就是用这种办法
可以有三种办法来取这些返回值和output参数
VretVal = pCmd->Parameters->GetItem(_bstr_t(\"Return\"))->Value;
VretVal = pCmd->Parameters->GetItem(short(0))->Value;
VretVal = retParam->Value;
都是一样的
6. 关于Refresh
关于这个函数,作用是
Command 对象去重新索取要操作的存储过程所有有关参数的信息,并且清空在refresh之前获取的参数信息。
所以一但调用它以后,就可以获取SP的那些信息,那些参数的信息,可以通过pCmd->Parameters->Item[_variant_t(_bstr_t(\"@pin1\") )]->Value=_variant_t(3); 这样的方式来设置参数
如果要使用Parameter的办法的话,就不要用refresh了,事实上,如果先把这些参数的值设置好了,如 inParam2->Value = _variant_t(_T(\"DD1\"));,一旦refresh后,这些参数就没有意义了。
7.关于pConn->CursorLocation =adUseClient;
设置这个东西,如果不设置游标为adUseClient,那么我在取return和output参数的时候,必须在我把记录集关闭以后才能取,就是说,必须先取记录集,然后关闭它,最后再取return和output参数,如果我在关闭记录集之前就取return和output的值,那么就不能取到正确的值,设成adUseClient就ok了,另外有一点就是,Execute 方法返回的游标继承该设置。Recordset 将自动从与之关联的连接中继承该设置。我把pConn设成adUseClient,那么最后,我的记录集也是adUseClient的了。
在可以执行这个存储过程了
m_pRecordset = m_pCommand->Execute(0,0,adCmdStoredProc);
这个时候,如果接下来用
_variant_t ret_val = m_pCommand->Parameters->GetItem((long)0)->Value;
那么将得不到值
而如果像下面这样调用的话就可以得到返回值了
m_pRecordset->Close();
_variant_t output_para = m_pCommand->Parameters->GetItem((long)0)->Value;
MS ADO.net给这一现象的回复是:
You can think of a stored procedure as a function in your code. The function doesn’t return a value until it has executed all of its code. If the stored procedure returns results and you haven’t finished processing these results, the stored procedure hasn’t really finished executing. Until you’ve closed the DataReader, the return and output parameters of your Command won’t contain the values returned by your stored procedure.
也就是说Execute()函数应该看成是直到m_pRecordset关掉以后才会正确返回.
关于输出参数的处理也和这一样,因为返回值本身就是当成输出参数来处理的.
通过这种方法,我们可以得到一个存储过程的返回值和结果集,而且对于所有的存储过程都可以一样使用,不必为某个特定的存储过程去写代码,具有一定的通用性.