在存储过程中接收少数结果集中的一个

Receiving one out of few Result Sets in stored procedure

我有一个工作正常的存储过程,但它里面有三个 "select"s。

select 不是来自内部临时 table。

这个主要是程序的格式:

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

我正在执行另一个过程中的过程,需要在那里捕获它。

我创建了一个 table 并从程序中插入了 "exec",就像这样:

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'

之后我收到这个错误:

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

table 的列的顺序和名称与过程 returns 完全相同。

是否有可能只捕获过程 returns 中的一个 table 而避免另一个?我根本无法更改程序。

我尝试声明一个 table 与过程的第一个 select 相同的字段,但我收到一条错误消息说

提前致谢!

有一种方法可以获得第一个记录集,但恐怕你运气不好。

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

编辑: 如果您只需要检查其他结果集的列不为空,您可以像这样预定义预期的结果集:

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

如果存储过程中的查询 returns 为空值,则会在过程中的那个点引发异常。这仅适用于 sql 服务器 2012 及更高版本。

如果所有 returned 的结果集都具有相同的结构,那么您可以将它们转储到临时 table,就像您尝试做的那样。但是,这只能让你走到这一步,因为如果字段中的数据不能用于确定特定行来自哪个结果集,那么你只有所有的结果集,无法过滤掉你不想要的结果集想要。

与多个结果集单独交互的唯一方法是通过应用程序代码(即客户端连接),而不管它们具有相同或不同的结构。如果您想在另一个查询的上下文中执行此操作,则需要使用 SQLCLR.

下面的 C# 代码显示了一个 SQLCLR 存储过程,它将执行一个 T-SQL 存储过程,returns 4 个结果集。它跳过前 2 个结果集,仅 returns 第三个结果集。这允许 SQLCLR 存储过程根据需要在 INSERT...EXEC 中使用。

以下代码调用的T-SQL存储过程的代码显示在C#代码块下方。 T-SQL 测试过程执行 sp_who2 并且只有 return 字段的子集被该过程 return 编辑,表明您不需要 return 与您正在阅读的结果集完全相同;它可以在运输过程中被操纵。

C# SQLCLR 过程:

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 测试过程:

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;

待办事项:

  • 适当调整_ResultSetsToSkip。如果您只想要第一个结果集,只需删除 _ResultSetsToSkipfor 循环。

  • 适当定义_ResultSet

  • 设置_Command.CommandText为"spDebuggerViews_GetStuckWorkflowInstances"

  • 通过SqlParameter(即@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')创建必要的参数

  • 如果需要,将输入参数添加到 SQLCLR 过程,以便它们可用于设置某些 SqlParameters

    [= 的值60=]

然后使用如下:

INSERT INTO #stalledp 
         (RowNumber,fldid,fldLastUpdated,fldCreationDate,fldName,fldPending)
EXEC Get3rdResultSetFromGetStuckWorkflowInstances;