如何将临时表作为参数传递到单独的存储过程中

Posted

技术标签:

【中文标题】如何将临时表作为参数传递到单独的存储过程中【英文标题】:How to pass a temp table as a parameter into a separate stored procedure 【发布时间】:2013-11-20 19:43:26 【问题描述】:

我有一个存储过程,它接受一个输入参数@CategoryKeys varchar,并将其内容解析到一个临时表#CategoryKeys

        -- create the needed temp table.
        CREATE TABLE #CategoryKeys
          (
             CategoryKey SMALLINT
          );

        -- fill the temp table if necessary
        IF Len(rtrim(ltrim(@CategoryKeys))) > 0
          BEGIN
              INSERT INTO #CategoryKeys
                          (CategoryKey)
              SELECT value
              FROM   dbo.String_To_SmallInt_Table(@CategoryKeys, ',');
          END

如果临时表有行,我想将表传递到单独的存储过程中。我将如何在单独的过程中创建一个参数来保存临时表?

【问题讨论】:

您是否考虑过使用table-valued parameters,而不是首先传递逗号分隔的列表?你也读过this article吗? 或 XML,因为 SQL Server 提供对 XML 数据类型的支持... 【参考方案1】:

虽然了解范围界定解决了直接需求,但认为在组合中添加更多选项以详细说明 cmets 的建议可能会很有用。

    将 XML 传递到存储过程中 将表值参数传递到存储过程中

1.将 XML 传递到存储过程中

通过将 XML 传递到参数中,您可以直接在 SQL 查询中使用 XML 并加入/应用于其他表:

CREATE PROC sp_PassXml
    @Xml XML
AS
BEGIN
    SET NOCOUNT ON
    SELECT T.Node.value('.', 'int') AS [Key]
    FROM @Xml.nodes('/keys/key') T (Node)
END
GO

然后调用存储过程进行测试:

DECLARE @Text XML = '<keys><key>1</key><key>2</key></keys>'
EXEC sp_PassXml @Text

简单查询的示例输出。

Key
-----------
1
2

2。将表值参数传递到存储过程中

首先,您必须为存储过程使用的表变量定义用户定义类型。

CREATE TYPE KeyTable AS TABLE ([Key] INT)

然后,您可以将该类型用作存储过程的参数(READONLY 是必需的,因为仅支持 IN 并且无法更改表)

CREATE PROC sp_PassTable
    @Keys KeyTable READONLY
AS
BEGIN
    SET NOCOUNT ON
    SELECT * FROM @Keys
END
GO

然后可以使用表变量直接从 SQL 调用存储过程。

DECLARE @Keys KeyTable
INSERT @Keys VALUES (1), (2)
EXEC sp_PassTable @Keys

注意:如果您使用的是 .NET,则可以从与用户定义类型匹配的 DataTable 类型中传递 SQL 参数。

查询的示例输出:

Key
-----------
1
2

【讨论】:

虽然我已经接受了这个问题的答案,但我对此表示赞同,因为“仅仅因为范围有效”......并不意味着它是一个好的解决方案。将#tempTables 视为全局变量是不好的常规做法。而我正是这样做的,我一直通过 xml 来促进基于集合的操作以避免游标和 RBAR。 这应该是公认的答案:即使它没有直接回答问题【参考方案2】:

当您创建 #TEMP 表时,“范围”不仅仅是创建它的过程。

下面是一个示例:

IF EXISTS 
    (
    SELECT * FROM INFORMATION_SCHEMA.ROUTINES
    WHERE ROUTINE_TYPE = N'PROCEDURE' and ROUTINE_SCHEMA = N'dbo' and ROUTINE_NAME = N'uspProc002'  
    )
BEGIN
    DROP PROCEDURE [dbo].[uspProc002]
END


GO

CREATE Procedure dbo.uspProc002 
AS

BEGIN

/* Uncomment this code if you want to be more explicit about bad "wiring" */
/*
IF OBJECT_ID('tempdb..#TableOne') IS NULL
begin
        THROW 51000, 'The procedure expects a temp table named #TableOne to already exist.', 1;  
end
*/

    /* Note, I did not Create #TableOne in this procedure.  It "pre-existed".  An if check will ensure that it is there.  */
    IF OBJECT_ID('tempdb..#TableOne') IS NOT NULL
    begin
        Insert into #TableOne ( SurrogateKey , NameOf ) select 2001, 'uspProc002'
    end

END


GO



IF EXISTS 
    (
    SELECT * FROM INFORMATION_SCHEMA.ROUTINES
    WHERE ROUTINE_TYPE = N'PROCEDURE' and ROUTINE_SCHEMA = N'dbo' and ROUTINE_NAME = N'uspProc001'  
    )
BEGIN
    DROP PROCEDURE [dbo].[uspProc001]
END


GO

CREATE Procedure dbo.uspProc001 (
@Param1 int
)
AS

BEGIN


    IF OBJECT_ID('tempdb..#TableOne') IS NOT NULL
    begin
            drop table #TableOne
    end


    CREATE TABLE #TableOne
    ( 
    SurrogateKey int , 
    NameOf varchar(12)
    )

    Insert into #TableOne ( SurrogateKey , NameOf ) select 1001, 'uspProc001'

    Select * from #TableOne

    EXEC dbo.uspProc002 

    Select * from #TableOne

    IF OBJECT_ID('tempdb..#TableOne') IS NOT NULL
    begin
            drop table #TableOne
    end


END


GO




exec dbo.uspProc001 0

话虽如此,请不要编写很多代码。它是全局变量的 SQL 等价物,难以维护且容易出错。

【讨论】:

【参考方案3】:

使用临时表的存储过程

CREATE OR ALTER Procedure Engine.TestProcTempTable
AS
BEGIN
  --DROP TABLE IF EXISTS #TestProcTempTable ;
  SELECT * from #TestProcTempTable;
END

创建将数据放入临时表中,供 SP 使用

DROP TABLE IF EXISTS #TestProcTempTable ;
select * into #TestProcTempTable from <TABLE_NAME>;
execute Engine.TestProcTempTable

【讨论】:

以上是关于如何将临时表作为参数传递到单独的存储过程中的主要内容,如果未能解决你的问题,请参考以下文章

临时表作为存储过程中的输出参数

如何在没有临时表的情况下从另一个存储过程调用存储过程(带参数)

T-SQL 中具有表参数的存储过程

如何将C#数据表传递给mysql的存储过程参数

在 PL/SQL 存储过程中将表作为参数传递

我们如何避免并行执行存储过程?