1

I have a stored procedure that works fine but it has inside it three "select"s.

The selects are not from an inner temporary table.

This is mainly the format of the procedure:

ALTER PROCEDURE [dbo].[STProce]  
@param1 int,
@param2 int,
@param3 int,
@param4 int,
@param5 int
AS  

select @param1 as p1, @param2 as p2, @param3 as p3

.
.
.

select @param4 as p4

.
.
.

select @param5 as p5

I'm executing the procedure from another procedure and need to catch it there.

I created a table and inserts into it the "exec" from the procedure, like that:

CREATE TABLE #stalledp
(
   RowNumber INT,
   fldid INT,
   fldLastUpdated datetime,
   fldCreationDate datetime,
   fldName nvarchar(255),
   fldPending nvarchar(255)
)

INSERT INTO #stalledp (RowNumber,fldid,fldLastUpdated,fldCreationDate,fldName,fldPending)
EXEC spDebuggerViews_GetStuckWorkflowInstances @workflowSpaceId='00000000-0000-0000-0000-000000000000',@pageNum=1,@pageSize=100000,@orderByColumn=N'fldid',@sortOrder=1,@workflowInstanceId=0,@stuckInstanceType=1,@createdDateFrom='1900-01-01 00:00:00',@createdDateTo='9999-01-01 23:59:59',@updatedDateFrom='1900-01-01 00:00:00',@updatedDateTo='9999-01-01 23:59:59'

Afterwards I receive this error:

Column name or number of supplied values does not match table definition.

The order and name of columns of the table is exactly like the procedure returns.

Is there a possibility to catch only one of the tables that the procedure returns and avoid the other? I cannot change the procedure at all.

I tried declaring a table the same fields as the first select of the procedure and I get an error says that

Thank you in advance!

Solomon Rutzky
  • 46,688
  • 9
  • 128
  • 171
Itzick Binder
  • 175
  • 1
  • 4
  • 18
  • can you share the stored proc? – SMA Feb 15 '15 at 09:06
  • If by catching you mean storing the results somewhere, Insert into @tmp exec procedure will capture the first result set. If you need something else, I think you'll going to have to change the procedure – James Z Feb 15 '15 at 09:27
  • II'm afraid in a t-sql procedure you can only capture the first returned resultset from another procedure. To capture more than one result set you need a clr-procedure. – Jesús López Feb 15 '15 at 09:55
  • If the select had same columns count and type you could insert all of them into a temp table then make a query on the temp table to retrieve second result for example – Reza Feb 15 '15 at 10:43
  • @JamesZ The existing stored proc does not need to change. As JesúsLópez mentioned in his comment, this can be done via SQLCLR. I gave an example of it in my [answer](http://stackoverflow.com/a/28546729/577765). – Solomon Rutzky Aug 12 '15 at 20:30
  • @JesúsLópez You are correct. SQLCLR is the only way to accomplish getting something other than the first result set. I gave an example of it in my [answer](http://stackoverflow.com/a/28546729/577765). – Solomon Rutzky Aug 12 '15 at 20:32
  • @RezaRahmati While it is true that if all of the result sets had the same schema then they could all go into a temp table, it would not be possible to distinguish which rows came from which result set. You would have to modify the stored procedure to add a dummy column that would be used to allow for that filtering. On the other hand, using SQLCLR it is possible to get any particular result set, regardless of any differences in schema among them. I provided an example in my [answer](http://stackoverflow.com/a/28546729/577765). – Solomon Rutzky Aug 12 '15 at 20:42

2 Answers2

0

If all of the result sets returned are of the same structure, then you can dump them to a temp table as you are trying to do. However, that only gets you so far because if the data in the fields cannot be used to determine which result set a particular row came from, then you just have all of the result sets with no way to filter out the ones you don't want.

The only way to interact with multiple result sets individually, regardless of them having the same or differing structures, is through app code (i.e. a client connection). And if you want to do this within the context of another query, then you need to use SQLCLR.

The C# code below shows a SQLCLR stored procedure that will execute a T-SQL stored procedure that returns 4 result sets. It skips the first 2 result sets and only returns the 3rd result set. This allows the SQLCLR stored procedure to be used in an INSERT...EXEC as desired.

The code for the T-SQL stored proc that is called by the following code is shown below the C# code block. The T-SQL test proc executes sp_who2 and only return a subset of the fields being returned by that proc, showing that you don't need to return the exact same result set that you are reading; it can be manipulated in transit.

C# SQLCLR proc:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public class TheProc
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void Get3rdResultSetFromGetStuckWorkflowInstances()
    {
        int _ResultSetsToSkip = 2; // we want the 3rd result set

        SqlConnection _Connection = null;
        SqlCommand _Command = null;
        SqlDataReader _Reader = null;

        try
        {
            _Connection = new SqlConnection("Context Connection = true;");
            _Command = _Connection.CreateCommand();

            _Command.CommandType = CommandType.StoredProcedure;
            _Command.CommandText = "tempdb.dbo.MultiResultSetTest";
            // (optional) add parameters (but don't use AddWithValue!)

            // The SqlDataRecord will be used to define the result set structure
            // and act as a container for each row to be returned
            SqlDataRecord _ResultSet = new SqlDataRecord(
                new SqlMetaData[]
                {
                    new SqlMetaData("SPID", SqlDbType.Char, 5),
                    new SqlMetaData("Status", SqlDbType.NVarChar, 30),
                    new SqlMetaData("Login", SqlDbType.NVarChar, 128),
                    new SqlMetaData("HostName", SqlDbType.NVarChar, 128),
                    new SqlMetaData("BlkBy", SqlDbType.VarChar, 5),
                    new SqlMetaData("DBName", SqlDbType.NVarChar, 128)
                });

            SqlContext.Pipe.SendResultsStart(_ResultSet); // initialize result set

            _Connection.Open();

            _Reader = _Command.ExecuteReader();

            // Skip a predefined number of result sets
            for (int _Index = 0;
                     _Index < _ResultSetsToSkip && _Reader.NextResult();
                     _Index++) ;

            // Container used to move 1 full row from the result set being read
            // to the one being sent, sized to the number of fields being read
            Object[] _TempRow = new Object[_Reader.FieldCount];

            while (_Reader.Read())
            {
                _Reader.GetValues(_TempRow);                // read all columns
                _ResultSet.SetValues(_TempRow);             // set all columns
                SqlContext.Pipe.SendResultsRow(_ResultSet); // send row
            }
        }
        catch
        {
            throw;
        }
        finally
        {
            if(SqlContext.Pipe.IsSendingResults)
            {
                SqlContext.Pipe.SendResultsEnd(); // close out result set being sent
            }

            if(_Reader != null && !_Reader.IsClosed)
            {
                _Reader.Dispose();
            }

            _Command.Dispose();

            if (_Connection != null && _Connection.State != ConnectionState.Closed)
            {
                _Connection.Dispose();
            }
        }

        return;
    }
}

T-SQL test proc:

USE [tempdb]
SET ANSI_NULLS ON;
IF (OBJECT_ID('dbo.MultiResultSetTest') IS NOT NULL)
BEGIN
    DROP PROCEDURE dbo.MultiResultSetTest;
END;
GO

CREATE PROCEDURE dbo.MultiResultSetTest
AS
SET NOCOUNT ON;

SELECT 1 AS [ResultSet], 'asa' AS [test];

SELECT 2 AS [ResultSet], NEWID() AS [SomeGUID], GETDATE() AS [RightNow];

EXEC sp_who2;

SELECT 4 AS [ResultSet], CONVERT(MONEY, 131.12) AS [CashYo];
GO

EXEC tempdb.dbo.MultiResultSetTest;

To do:

  • Adjust _ResultSetsToSkip as appropriate. If you only want the first result set, simply remove both _ResultSetsToSkip and the for loop.

  • Define _ResultSet as appropriate

  • Set _Command.CommandText to be "spDebuggerViews_GetStuckWorkflowInstances"

  • Create the necessary parameters via SqlParameter (i.e. @workflowSpaceId='00000000-0000-0000-0000-000000000000',@pageNum=1,@pageSize=100000,@orderByColumn=N'fldid',@sortOrder=1,@workflowInstanceId=0,@stuckInstanceType=1,@createdDateFrom='1900-01-01 00:00:00',@createdDateTo='9999-01-01 23:59:59',@updatedDateFrom='1900-01-01 00:00:00',@updatedDateTo='9999-01-01 23:59:59')

  • If needed, add input parameters to the SQLCLR proc so that they can be used to set the values of certain SqlParameters

Then use as follows:

INSERT INTO #stalledp 
         (RowNumber,fldid,fldLastUpdated,fldCreationDate,fldName,fldPending)
EXEC Get3rdResultSetFromGetStuckWorkflowInstances;
Solomon Rutzky
  • 46,688
  • 9
  • 128
  • 171
-1

There is a way to get the first record set but the others, I'm afraid, you're out of luck.

EXEC sp_addlinkedserver @server = 'LOCALSERVER',  @srvproduct = '',
                        @provider = 'SQLOLEDB', @datasrc = @@servername
SELECT * FROM OPENQUERY(LOCALSERVER, 'EXEC testproc2')

EDIT: If you only need to check the other result set for columns to be not null you could predefine the expected results sets like so:

EXEC testproc2 WITH RESULT SETS (
        (a VARCHAR(MAX) NOT NULL, b VARCHAR(MAX) NOT NULL), 
        (a VARCHAR(MAX) NOT NULL)
);

If the query within the stored procedure returns null values a exception is raised at that point in procedure. This will only work on sql server 2012 and upwards though.

MWillemse
  • 960
  • 5
  • 9