Symptoms
There are important issues to consider when attempting to retrieveRAISERROR/PRINT/RETURN values from SQL Server stored procedures throughActiveX Data Objects (ADO). Here are three issues:RAISERROR statements in SQL Server must be a severity level of 11-18.PRINT statements in SQL Server can also populate the ADO errorscollection. However, PRINT statements are severity level zero (0) so, atleast one RAISERROR statement is required in the stored procedure toretrieve a PRINT statement with ADO through the Errors collection.RETURN values in a stored procedure must be associated with at least oneresultset.
Resolution
The following code sample demonstrates browsing the ADO Errors collectionto access the RAISERROR/PRINT/RETURN detail from a SQL Server storedprocedure returning multiple resultsets:Paste and execute the following code in the ISQL_W window to create thestored procedure used for the ADO sample in step 4:
use pubsGOif exists (select * from sysobjects where id =object_id(‘dbo.ADOTestRPE’) and sysstat & 0xf = 4)drop procedure dbo.ADOTestRPEGOcreate procedure ADOTestRPE(@SetRtnINT=0 OUTPUT,@R1NumINT=1,@P1NumINT=1,@E1NumINT=1,@R2NumINT=2,@P2NumINT=2,@E2NumINT=2)ASDECLARE @iLoopINTDECLARE @PrintText VARCHAR(255)DECLARE @iErrNumINT/*Check for no Resultsets – needed to get the RETURN value back */IF @R1Num + @R2Num = 0 SELECT NULL/*Resultset 1******************************* */IF @R1Num > 0BEGINSET ROWCOUNT @R1NumSELECT ‘Resultset 1′ RsNum, TitleFROM Pubs..TitlesSET ROWCOUNT 0END/* Must raise a default error context in which to return the PRINT *//*statement *//* (if none present) since PRINT statements are a severity level of *//*0. */IF (@P1Num > 0) AND (@E1Num = 0) RAISERROR (“RAISERROR.PError1″,11, 2)IF @P1Num > 0BEGINSELECT @iLoop = 0WHILE @iLoop < @P1NumBEGINSELECT @iLoop = @iLoop + 1SELECT @PrintText = ‘PRINT.Resultset.1: Line ‘ +CONVERT(char(2), @iLoop)PRINT @PrintTextENDENDIF @E1Num > 0BEGINSELECT @iLoop = 0WHILE @iLoop < @E1NumBEGINSELECT @iLoop = @iLoop + 1SELECT @iErrNum = @iLoop + 201000RAISERROR (“RAISERROR.Resultset.1″, 11, 2)ENDEND/*Resultset 2******************************* */IF @R2Num > 0BEGINSET ROWCOUNT @R2NumSELECT ‘Resultset 2′ RsNum, TitleFROM Pubs..TitlesSET ROWCOUNT 0END/* Must raise a default error context in which to return the PRINT *//*statement *//* (if none present) since PRINT statements are a severity level of *//*0. */IF (@P2Num > 0) AND (@E2Num = 0) RAISERROR (“RAISERROR.PError2″,11, 2)IF @P2Num > 0BEGINSELECT @iLoop = 0WHILE @iLoop < @P2NumBEGINSELECT @iLoop = @iLoop + 1SELECT @PrintText = ‘PRINT.Resultset.2: Line ‘ +CONVERT(char(2), @iLoop)PRINT @PrintTextENDENDIF @E2Num > 0BEGINSELECT @iLoop = 0WHILE @iLoop < @E2NumBEGINSELECT @iLoop = @iLoop + 1SELECT @iErrNum = @iLoop + 202000RAISERROR (“RAISERROR.Resultset.2″, 11, 2)ENDEND/*Return & Output ************************************ */select @SetRtn = -1RETURN @SetRtnGO Create a Standard .EXE project in Visual Basic. Form1 is created bydefault.From the Project menu, choose References and select the MicrosoftActiveX Data Objects Library.NOTE: You must use ADO version 2.0 or later for the code to work correctly. You can obtain the latest Microsoft Data Access Components (MDAC) components on the Web at the following URL:
http://msdn.microsoft.com/en-us/data/aa937729.aspx(http://msdn.microsoft.com/en-us/data/aa937729.aspx)Place a Command button on the Form, and then paste the following code inthe General Declarations section of the Form:NOTE: You may need to change the database connect string for yourenvironment.
‘This Code demonstrates RAISERROR/PRINT/RETURN values with ADO and’multiple resultsets.Sub CreateParms()Dim ADOCmd As New ADODB.CommandDim ADOPrm As New ADODB.ParameterDim ADOCon As ADODB.ConnectionDim ADORs As ADODB.RecordsetDim sParmName As StringDim strConnect As StringDim rStr As StringOn Error GoTo ErrHandlerstrConnect = “driver={SQLServer};server=(local);uid=sa;pwd=;database=pubs”Set ADOCon = New ADODB.ConnectionWith ADOCon.Provider = “MSDASQL”.CursorLocation = adUseServer’Must use Server side cursor..ConnectionString = strConnect.OpenEnd WithSet ADOCmd.ActiveConnection = ADOConWith ADOCmd.CommandType = adCmdStoredProc.CommandText = “ADOTestRPE”End With’Parameter 0 is the stored procedure Return code.sParmName = “Return”Set ADOPrm = ADOCmd.CreateParameter(sParmName, adInteger, _adParamReturnValue, , 0)ADOCmd.Parameters.Append ADOPrmADOCmd.Parameters(sParmName).Value = -1′Parameter 1 is the setting for the stored procedure Output’ parameter.sParmName = “Output”Set ADOPrm = ADOCmd.CreateParameter(sParmName, adInteger, _adParamOutput)ADOCmd.Parameters.Append ADOPrmADOCmd.Parameters(sParmName).Value = 999′Parameter 2sParmName = “R1Num”‘Number of rows to return in Resultset 1.Set ADOPrm = ADOCmd.CreateParameter(sParmName, adInteger, _adParamInput)ADOCmd.Parameters.Append ADOPrmADOCmd.Parameters(sParmName).Value = 1′Parameter 3sParmName = “P1Num”‘Number of PRINT statements in Resultset 1.Set ADOPrm = ADOCmd.CreateParameter(sParmName, adInteger, _adParamInput)ADOCmd.Parameters.Append ADOPrmADOCmd.Parameters(sParmName).Value = 0′Parameter 4sParmName = “E1Num”‘Number of RAISERROR statements in Resultset’1.Set ADOPrm = ADOCmd.CreateParameter(sParmName, adInteger, _adParamInput)ADOCmd.Parameters.Append ADOPrmADOCmd.Parameters(sParmName).Value = 0′Parameter 5sParmName = “R2Num”‘Number of rows to return in Resultset 2.Set ADOPrm = ADOCmd.CreateParameter(sParmName, adInteger, _adParamInput)ADOCmd.Parameters.Append ADOPrmADOCmd.Parameters(sParmName).Value = 2′Parameter 6sParmName = “P2Num”‘Number of PRINT statements in Resultset 2.Set ADOPrm = ADOCmd.CreateParameter(sParmName, adInteger, _adParamInput)ADOCmd.Parameters.Append ADOPrmADOCmd.Parameters(sParmName).Value = 0′Parameter 7sParmName = “E2Num”‘Number of RAISERROR statements in Resultset’ 2.Set ADOPrm = ADOCmd.CreateParameter(sParmName, adInteger, _adParamInput)ADOCmd.Parameters.Append ADOPrmADOCmd.Parameters(sParmName).Value = 0Set ADORs = ADOCmd.ExecuteDo While (Not ADORs Is Nothing)If ADORs.State = adStateClosed Then Exit DoWhile Not ADORs.EOFFor i = 0 To ADORs.Fields.Count – 1rStr = rStr & ” : ” & ADORs(i)Next iDebug.Print Mid(rStr, 3, Len(rStr))ADORs.MoveNextrStr = “”WendDebug.Print “———————-”Set ADORs = ADORs.NextRecordsetLoopDebug.Print “Return: ” & ADOCmd.Parameters(“Return”).ValueDebug.Print “Output: ” & ADOCmd.Parameters(“Output”).ValueGoTo ShutdownErrHandler:Call ErrHandler(ADOCon)Resume NextShutdown:Set ADOCmd = NothingSet ADOPrm = NothingSet ADORs = NothingSet ADOCon = NothingEnd SubPrivate Sub Command1_Click()Call CreateParmsEnd SubSub ErrHandler(objCon As Object)Dim ADOErr As ADODB.ErrorDim strError As StringFor Each ADOErr In objCon.ErrorsstrError = “Error #” & ADOErr.Number & vbCrLf & ADOErr.Description _& vbCr & _”(Source: ” & ADOErr.Source & “)” & vbCr & _”(SQL State: ” & ADOErr.SQLState & “)” & vbCr & _”(NativeError: ” & ADOErr.NativeError & “)” & vbCrIf ADOErr.HelpFile = “” ThenstrError = strError & “No Help file available” & vbCr & vbCrElsestrError = strError & “(HelpFile: ” & ADOErr.HelpFile & “)” _& vbCr & _”(HelpContext: ” & ADOErr.HelpContext & “)” & _vbCr & vbCrEnd IfDebug.Print strErrorNextobjCon.Errors.ClearEnd Sub Change the value of parameters two through seven to alter the number ofPRINT statements and/or RAISERROR statements generated by the storedprocedure and returned through ADO. Run the Visual Basic code sampleagain and note that the RAISERROR and PRINT statements are returnedthrough the ADO errors collection. Change the values to experiment withdifferent combinations of PRINT/RAISERROR statements with differentresultsets. Please refer to the SQL stored procedures for specificworkarounds for special cases.
NOTE: To retrieve a RETURN value in ADO with a stored procedure theremust be at least one resultset. In order to work around this problem,when no resultsets are specified (in the ADO sample code) the storedprocedure executes a SELECT NULL to return a null resultset to ADOthereby populating the RETURN value. In addition, to work around theissue of specifying no RAISERROR statements and a combination of PRINTstatements, default RAISERROR statements are generated in order toprovide a context for returning the PRINT statement via ADO. You mustcode RAISERROR statements in the format shown in the stored procedurebecause only severity levels of 11-18 return through the ADO errorscollection.