AlphaDAO - Stored Procedures and Output Arguments on the SQL Server

Description

If you have a SQL server stored procedure that sets the value of an output argument, reading the value in the output argument, after the stored procedure has executed can be tricky. That's because you have to ensure that you first loop through all of the result sets that are returned by the stored procedure before reading the output arguments.

Consider the following trivial stored procedure:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE TestSP
-- Add the parameters for the stored procedure here
@InParam CHAR(10),
@OutMessage CHAR(50) OUTPUT

AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET @OutMessage = 'initialized'

END
GO

From the interactive window:

dim cn as sql::Connection
cn.open("::Name::sqlserver")
delete args 
dim args as sql::Arguments
dim Message as c = "test message"
'need to pad out the argument so it matches the definition in the stored procedure
message = padr(Message,50," ")
args.set("InParam" , "1234567890")
args.set("OutMessage",Message,sql::ArgumentUsage::InputOutputArgument)
?cn.Execute("{CALL TestSP(:InParam, :OutMessage)}",args)
= .T.

Now try to read the output parameter value

?args[2].data
= "Initialized"

Notice that this worked as expected. But it only worked because the stored procedure did not return any result sets.

Now let's modify the stored procedure so that it returns two result sets:

USE [Northwind]
GO
/****** Object: StoredProcedure [dbo].[TestSP] Script Date: 12/6/2013 1:25:13 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[TestSP]
-- Add the parameters for the stored procedure here
@InParam CHAR(10),
@OutMessage CHAR(50) OUTPUT

AS
BEGIN
select * from customers 
select * from employees
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
select * from customers 
SET @OutMessage = 'initialized'

END

Now, From the interactive window:

dim cn as sql::Connection
cn.open("::Name::sqlserver")
delete args 
dim args as sql::Arguments
dim Message as c = "test message"
'need to pad out the argument so it matches the definition in the stored procedure
message = padr(Message,50," ")
args.set("InParam" , "1234567890")
args.set("OutMessage",Message,sql::ArgumentUsage::InputOutputArgument)
?cn.Execute("{CALL TestSP(:InParam, :OutMessage)}",args)
= .T.

Now try to read the output parameter value

?args[2].data 
= "test message"

This is not what we expect! The output parameter cannot yet be read because we have not yet looped through all of the result sets returned by the stored procedure.

So, execute:

?cn.ResultSet.NextResult()
= .t.

and again:

?cn.ResultSet.NextResult()
= .t.

and again (the .f. return value tells you that there are no more resultsets):

?cn.ResultSet.NextResult()
= .f.

Now, that there are no more resultsets, you can read the output arguments:

?args[2].data
= "initialized"