Skip to content

shannonlowder.com

Menu
  • About
  • Biml Interrogator Demo
  • Latest Posts
Menu

SQL201 – Stored Procedure Results

Posted on September 26, 2011 by slowder

I’m working my way through the last of the 201 lessons. Today we’re going to dive back into stored procedures. This time I want to cover the three ways you can get data back out. You can simply use a SELECT statement to return a record set, or you can define one or more of your procedure’s parameters as an OUTPUT variable. The last method you can use is a return code. The return code can seem like a limited way of returning data, but if you think your return codes through before implementing the procedure, you can pull off some useful tricks!

Record Sets

This is the most common way I’ve seen people pull data out of a stored procedure. If you wrap a SELECT statement into a stored procedure the record set is returned. The problem with using this method is, you have to be careful in how the code calling the stored procedure will deal with multiple record sets.

Most of the time, people will call a stored procedure and expect only one record set. But look at the query below

CREATE PROCEDURE usp_ExampleWithMultiplerecord sets
AS
BEGIN
SELECT '1' AS ID, 'This is the first record set.' as Message

SELECT '2' AS ID, 'This is the second record set.' as Message
END
GO
EXEC usp_ExampleWithMultiplerecord sets

When you run this stored procedure you get two different record sets. If you were calling this from a webpage, if you don’t handle the results right, you might only see the first record set.

This gets even trickier if you do a DML (Data Manipulation Language) query and then a SELECT.

CREATE PROCEDURE usp_ExampleWithDMLAndQuery
AS
BEGIN
DECLARE @results TABLE ( ID INT, Message VARCHAR(255) )

INSERT INTO @results
SELECT '1' AS ID, 'This is the first record set.' as Message
SELECT '2' AS ID, 'This is the second record set.' as Message

SELECT * FROM @results
END
GO
EXEC usp_ExampleWithDMLAndQuery

This time you only see one record set, but look at the messages in SSMS. Notice you see a message:

"2 rows affected"

Consider the webpage example again. If your webpage doesn’t interpret the results just right, you won’t see the two rows. You’ll keep seeing a NULL record set. That’s because your page is interpreting the “2 rows affected” as the first record set. The common workaround is to add a SET NOCOUNT ON statement to the beginning of your stored procedure, but that’s just a band-aid. The real solution is to look at how you’re interpreting the stored procedures results in the web page (or application layer).

Output Parameters

After a while of using just record sets to return data from stored procedures, I see people “upgrading” their code to use output parameters. It’s very easy to set up a stored procedure to use an output parameter, you simply add the keyword OUTPUT after the parameter’s data type, and you’re set. The only trick to know is when you call the stored procedure, you have to set up a variable outside the stored procedure to hold the returned value, and you have to then identify that variable as an OUTPUT variable in order for the SQL interpreter to pass the values off to your variable. Let’s work through an example now.

CREATE PROCEDURE usp_multiplyTwoNumbers
@input1 INT
, @input2 INT
, @result INT OUTPUT
AS
BEGIN
SET @result = @input1 * @input2
END
GO

After running this code, you have a stored procedure that will multiply the two input values, and store the result in an OUTPUT parameter called @result. Now, that we have the stored procedure, let’s use it.

DECLARE @currentResult INT

EXEC usp_multiplyTwoNumbers 2, 2, @currentResult OUTPUT

SELECT 'The result is " + CONVERT(VARCHAR(255), @currentResult)

We multiplied 2 * 2 and stored the result in @currentResult. We then showed the results along with some explanatory notes. We could use the variable any way we wish. The value is there and accessible until the script ends, or you reach a GO statement. Then you go outside the scope of that variable.

With some time and practice, you’ll find all sorts of useful ways to use OUTPUT parameters. Things get really interesting once you consider table variables!
Return Codes

The last way I know of getting data out of a stored procedure is return codes. A return code is an set of integers you define in your stored procedure. I use them to indicate different types of errors or warnings that can occur when a stored procedure is run.

Let’s look at one of the examples from BOL:

USE AdventureWorks;
GO
IF OBJECT_ID('Sales.usp_GetSalesYTD', 'P') IS NOT NULL
DROP PROCEDURE Sales.usp_GetSalesYTD;
GO
CREATE PROCEDURE Sales.usp_GetSalesYTD
@SalesPerson nvarchar(50) = NULL, -- NULL default value
@SalesYTD money = NULL OUTPUT
AS

-- Validate the @SalesPerson parameter.
IF @SalesPerson IS NULL
BEGIN
PRINT 'ERROR: You must specify a last name for the sales person.'
RETURN(1)
END
ELSE
BEGIN
-- Make sure the value is valid.
IF (SELECT COUNT(*) FROM HumanResources.vEmployee
WHERE LastName = @SalesPerson) = 0
RETURN(2)
END
-- Get the sales for the specified name and
-- assign it to the output parameter.
SELECT @SalesYTD = SalesYTD
FROM Sales.SalesPerson AS sp
JOIN HumanResources.vEmployee AS e ON e.EmployeeID = sp.SalesPersonID
WHERE LastName = @SalesPerson;
-- Check for SQL Server errors.
IF @@ERROR <> 0
BEGIN
RETURN(3)
END
ELSE
BEGIN
-- Check to see if the ytd_sales value is NULL.
IF @SalesYTD IS NULL
RETURN(4)
ELSE
-- SUCCESS!!
RETURN(0)
END

What we’ve defined here is a set of return codes that indicate where in the code we’ve gone wrong. We can now call this stored procedure like this:

DECLARE @returnCode INT
, @resultSalesYTD INT;

DECLARE @inputSalesPerson VARCHAR(50) = NULL;

EXEC @returnCode = EXEC Sales.usp_GetSalesYTD
@SalesPerson = @inputSalesPerson
, @SalesYTD = @resultSalesYTD OUTPUT

And we can programatically know how the stored procedure did, based on the value of @returnCode.

  • If @returnCode is 0, then we know the stored procedure executed successfully.
  • If we get a 1, then we know we didn’t pass in a valid LastName for the sales person.
  • If we get a 2, then we know that LastName doesn’t exist in the HumanResources.vEmployee view.
  • If we get a 3, we know we got some other SQL error.
  • If we get a 4, then we know the @salesYTD is going to be NULL.

Of course we would know if there was some other SQL error, since we didn’t implement any error trapping. So @returnCode 3 is superfluous. We could test for @result codes 2 and 4 other ways too, but I can think of none that wouldn’t require you to consider the output on the application side of things.

Conclusion

As always, knowing when to use each of the three options is key. Practice with them, and you’ll learn when each option is the best solution for the task at hand. As usual, if you have any questions send them in…I’m here to help!

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • A New File Interrogator
  • Using Generative AI in Data Engineering
  • Getting started with Microsoft Fabric
  • Docker-based Spark
  • Network Infrastructure Updates

Recent Comments

  1. slowder on Data Engineering for Databricks
  2. Alex Ott on Data Engineering for Databricks

Archives

  • July 2023
  • June 2023
  • March 2023
  • February 2023
  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • October 2018
  • August 2018
  • May 2018
  • February 2018
  • January 2018
  • November 2017
  • October 2017
  • September 2017
  • August 2017
  • June 2017
  • March 2017
  • February 2014
  • January 2014
  • December 2013
  • November 2013
  • October 2013
  • August 2013
  • July 2013
  • June 2013
  • February 2013
  • January 2013
  • August 2012
  • June 2012
  • May 2012
  • April 2012
  • March 2012
  • February 2012
  • January 2012
  • December 2011
  • November 2011
  • October 2011
  • September 2011
  • August 2011
  • July 2011
  • June 2011
  • May 2011
  • April 2011
  • March 2011
  • February 2011
  • January 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • August 2010
  • July 2010
  • June 2010
  • May 2010
  • April 2010
  • March 2010
  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • September 2009
  • August 2009
  • July 2009
  • June 2009
  • May 2009
  • April 2009
  • March 2009
  • February 2009
  • January 2009
  • December 2008
  • November 2008
  • October 2008
  • September 2008
  • August 2008
  • July 2008
  • June 2008
  • May 2008
  • April 2008
  • March 2008
  • February 2008
  • January 2008
  • November 2007
  • October 2007
  • September 2007
  • August 2007
  • July 2007
  • June 2007
  • May 2007
  • April 2007
  • March 2007
  • February 2007
  • January 2007
  • December 2006
  • November 2006
  • October 2006
  • September 2006
  • August 2006
  • July 2006
  • June 2006
  • May 2006
  • April 2006
  • March 2006
  • February 2006
  • January 2006
  • December 2005
  • November 2005
  • October 2005
  • September 2005
  • August 2005
  • July 2005
  • June 2005
  • May 2005
  • April 2005
  • March 2005
  • February 2005
  • January 2005
  • November 2004
  • September 2004
  • August 2004
  • July 2004
  • April 2004
  • March 2004
  • June 2002

Categories

  • Career Development
  • Data Engineering
  • Data Science
  • Infrastructure
  • Microsoft SQL
  • Modern Data Estate
  • Personal
  • Random Technology
  • uncategorized
© 2025 shannonlowder.com | Powered by Minimalist Blog WordPress Theme