将存储过程的结果插入到临时表中

Insert results of a stored procedure into a temporary table

提问人:Ferdeen 提问时间:3/17/2009 最后编辑:DineshDBFerdeen 更新时间:3/31/2023 访问量:2514358

问:

我该怎么做?不定义,没有定义?SELECT * INTO [temp table] FROM [stored procedure]FROM [Table][temp table]

Select来自INTO的所有数据都工作正常。BusinessLinetmpBusLine

select *
into tmpBusLine
from BusinessLine

我正在尝试相同的方法,但是使用返回数据的 a 并不完全相同。stored procedure

select *
into tmpBusLine
from
exec getBusinessLineHistory '16 Mar 2009'

输出消息:

消息 156,级别 15,状态 1,第 2 行 关键字附近的语法不正确 “执行”。

我已经阅读了几个创建与输出存储过程结构相同的临时表的示例,该表工作正常,但最好不提供任何列。

sql-server sql-server-2005 t-sql 存储过程

评论

25赞 Ferdeen 3/18/2009
使用 SELECT * INTO [TABLE NAME],您可以知道这些列,因为它们是从原始表中复制的。如果我要对存储过程执行相同的操作,这正是我想要的。
4赞 Triynko 6/2/2011
看到 sommarskog.se/share_data.html 和我的帖子 stackoverflow.com/questions/6215672/...
12赞 ktam33 7/26/2014
只想指出 “select * into tmpBusLine” 创建了一个永久表。您可能想要“select * into #tmpBusLine”。我敢肯定原始海报已经发现了这一点,但它可能会帮助其他找到这篇文章的人,因为它是目前搜索“选择进入临时表”的顶级结果
3赞 Wes Palmer 12/2/2016
我不知道这是否已得到解决,但出现错误的原因是因为 from 关键字。
36赞 kjmerf 5/26/2018
Microsoft 需要添加 SELECT * INTO FROM EXEC!请!

答:

320赞 Matt Hamilton 3/17/2009 #1

在 SQL Server 2005 中,可用于将存储过程的结果插入到表中。摘自 MSDN 的 INSERT 文档(实际上适用于 SQL Server 2000):INSERT INTO ... EXEC

--INSERT...EXECUTE procedure example
INSERT author_sales EXECUTE get_author_sales

评论

168赞 Ferdeen 3/17/2009
这需要预先定义authors_sales。我试图避免这种情况。谢谢。
6赞 Ferdeen 3/17/2009
我也是这么想的。非常有用 即时插入 tmp 表,但如果您需要知道从存储过程返回的数据集结构,则不太有用。谢谢你的帮助。
4赞 Rich Andrews 3/17/2009
这里有一篇好文章 msdn.microsoft.com/en-us/library/aa175921.aspx
6赞 Even Mien 4/12/2016
若要使用相同的架构,可以按如下方式进行复制:从 realTable 中选择 top 0 * 进入 tempTable (stackoverflow.com/a/9206463/73794)
4赞 BVernon 4/23/2020
@EvenMien当我看到你的评论时,我瞬间很兴奋......但可悲的是,只有当你的 proc 的结果实际上反映了一个真实的表:(
150赞 Quassnoi 3/17/2009 #2
Select @@ServerName
EXEC sp_serveroption @@ServerName, 'DATA ACCESS', TRUE

SELECT  *
INTO    #tmpTable
FROM    OPENQUERY(YOURSERVERNAME, 'EXEC db.schema.sproc 1')

评论

2赞 Ferdeen 3/17/2009
获取“消息 208,级别 16,状态 1,第 1 行无效的对象名称”tmpBusLine“(可能是因为它没有预先定义)。
1赞 Quassnoi 8/4/2009
@Ferds:对不起,一开始没听懂你的要求。更新了另一个解决方案。
31赞 jcollum 12/24/2009
很棒的解决方案。需要注意的是,您需要在服务器上启用“DATA ACCESS”:EXEC sp_serveroption“TheServerName”、“DATA ACCESS”、“TRUE”
10赞 BraveNewMath 5/7/2013
您还需要允许远程访问服务器。这将产生安全后果。
9赞 Sal 1/24/2017
如果目标存储过程使用临时表,则此操作将不起作用
131赞 dotjoe 3/17/2009 #3

当存储过程返回大量列并且您不想手动“创建”临时表来保存结果时,我发现最简单的方法是进入存储过程并在最后一个 select 语句上添加一个“into”子句,并将 1=0 添加到 where 子句。

运行存储过程一次,然后返回并删除刚刚添加的 SQL 代码。现在,您将拥有一个与存储过程的结果匹配的空表。您可以为临时表“将表脚本化为创建”,也可以直接插入到该表中。

评论

9赞 Ian Roke 8/27/2009
+1,很好的建议。您甚至可以向 sproc 添加一个名为 @TableCreate 或类似变量的快速可选变量,当不为 null 时,请执行上述步骤。一旦设置好,就不需要更改 sproc。
1赞 DotnetDude 4/25/2012
@dotjoe 您是否从临时表中执行临时表并执行脚本表作为创建?临时表显示,但我无法右键单击并执行创建脚本。任何帮助都是值得赞赏的。SELECT INTOtempdb
2赞 dotjoe 4/25/2012
@DotNetDude可以隐式创建实际表。select ... into new_table
0赞 user423430 11/16/2017
然后从空表架构中获取粗略的列定义;替换 '...'最后是合法的TABLE_NAME:declare @s varchar(max)='';select @s=@s+','+COLUMN_NAME+' '+DATA_TYPE+isnull('('+case CHARACTER_MAXIMUM_LENGTH when -1 then 'max' else cast(CHARACTER_MAXIMUM_LENGTH as varchar(10))end+')','')from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='...';select @s
1赞 Lucas925 10/4/2019
这是最好的解决方案!
736赞 Gavin 3/17/2009 #4

如果要在不先声明临时表的情况下执行此操作,可以尝试创建用户定义函数而不是存储过程,并使该用户定义函数返回表。或者,如果要使用存储过程,请尝试如下操作:

CREATE TABLE #tmpBus
(
   COL1 INT,
   COL2 INT
)

INSERT INTO #tmpBus
Exec SpGetRecords 'Params'

评论

230赞 Craig 4/27/2012
我认为关键是生成模式而不必显式声明它。
8赞 funkymushroom 6/2/2012
我很想知道这与上面@Aaron Alton 的解决方案有什么区别。这个似乎要简单得多,但我不确定任何其他含义。
14赞 Brady Holt 1/24/2014
这将起作用,但如果向 SpGetRecords 存储过程添加其他列,这将爆炸。
20赞 Matt Stephenson 4/9/2014
每个调用堆栈只能获得一个 INSERT INTO EXEC。SpGetRecords 和它调用的任何其他进程可能不会在其自己的代码中使用此策略。这可能会让 SpGetRecords 的维护者感到惊讶。
48赞 NickG 4/24/2015
这根本没有回答这个问题,我不明白为什么它如此受欢迎?OP 明确声明“未定义 [temp table]”,并且您的第一行有一个 create temp table 语句。
766赞 Aaron Alton 8/4/2009 #5

为此,可以使用 OPENROWSET。看一看。我还包含了启用 Ad Hoc Distributed Queries 的 sp_configure 代码,以防尚未启用。

CREATE PROC getBusinessLineHistory
AS
BEGIN
    SELECT * FROM sys.databases
END
GO

sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO

SELECT * INTO #MyTempTable FROM OPENROWSET('SQLNCLI', 'Server=(local)\SQL2008;Trusted_Connection=yes;',
     'EXEC getBusinessLineHistory')

SELECT * FROM #MyTempTable

评论

35赞 Rob Farley 8/5/2009
这是正确的方法。OPENROWSET 几乎是将存储过程的结果视为表表达式的唯一方法。
45赞 Ferdeen 8/10/2009
这似乎有点麻烦,只是插入到表格中。要做很多配置。另外,当我尝试它时,我得到“消息 7357,级别 16,状态 2,第 1 行无法处理对象”EXEC GetPartyAnalysisData 146”。链接服务器“(null)”的 OLE DB 访问接口“SQLNCLI”指示该对象没有列,或者当前用户对该对象没有权限。所以你需要设置一个链接服务器...
14赞 MartW 8/11/2009
不需要链接服务器,但需要正确获取连接字符串...此外,指定存储过程的完整路径,包括数据库名称和 SP 的所有者。
33赞 Tim Abell 11/18/2010
哎呀!对同一服务器的引用?讨厌。绝对比必须手动创建临时表更麻烦
31赞 greg 4/17/2013
我同意这是一个黑客攻击,除非你的背靠在墙上,否则应该避免。将 sp 更改为函数可能是一个更好的角度。恕我直言。
210赞 Christian Loris 8/5/2009 #6

这是对您的问题的略微修改版本的回答。如果可以放弃对用户定义函数使用存储过程,则可以使用内联表值用户定义函数。这本质上是一个存储过程(将接受参数),它返回一个表作为结果集;因此,将与 INTO 语句很好地放在一起。

这是一篇关于它和其他用户定义函数的很好的快速文章。如果仍然需要存储过程,则可以使用存储过程包装内联表值用户定义函数。存储过程在从内联表值用户定义函数调用 select * 时只传递参数。

例如,您将有一个内联表值的用户定义函数来获取特定区域的客户列表:

CREATE FUNCTION CustomersByRegion 
(  
    @RegionID int  
)
RETURNS TABLE 
AS
RETURN 
  SELECT *
  FROM customers
  WHERE RegionID = @RegionID
GO

然后,您可以调用此函数来获取您的结果:

SELECT * FROM CustomersbyRegion(1)

或者执行 SELECT INTO:

SELECT * INTO CustList FROM CustomersbyRegion(1)

如果仍需要存储过程,请按如下方式包装函数:

CREATE PROCEDURE uspCustomersByRegion 
(  
    @regionID int  
)
AS
BEGIN
     SELECT * FROM CustomersbyRegion(@regionID);
END
GO

我认为这是获得所需结果的最“无黑客”方法。它使用现有功能,因为它们打算在没有额外复杂性的情况下使用。通过将内联表值用户定义函数嵌套在存储过程中,可以通过两种方式访问该功能。加!对于实际的 SQL 代码,您只有一个维护点。

有人建议使用 OPENROWSET,但这不是 OPENROWSET 函数的用途(摘自联机丛书):

包括所有连接信息 访问远程数据所需的 从 OLE DB 数据源。这 方法是一种替代方法,可以替代访问 链接服务器中的表,并且是 一次性、临时的连接方法 以及使用 OLE 访问远程数据 FBl的如需更频繁地引用 OLE DB 数据源,使用链接 服务器。

使用 OPENROWSET 可以完成工作,但会因打开本地连接和封送数据而产生一些额外的开销。它也可能不是在所有情况下都是一个选项,因为它需要临时查询权限,这会带来安全风险,因此可能不需要。此外,OPENROWSET 方法将排除使用返回多个结果集的存储过程。将多个内联表值用户定义函数包装在单个存储过程中可以实现此目的。

评论

5赞 spencer7593 8/12/2010
+1 表值函数是一个合适的解决方案。我们应该注意一些小缺点:表值函数是一个额外的数据库对象,可能需要授予它的权限。
3赞 mrwaim 3/5/2011
喜欢这个解决方案。我遇到的一个小障碍是,我的表不能按存储过程中的顺序排列。好吧,我会解决的
7赞 mrwaim 3/6/2011
还有一个障碍 - “无法从函数内部访问临时表”
8赞 greg 4/17/2013
最初的问题是我们如何创建一个带有 sp 结果的临时表。这是一个很好的模式,但不能解决这个问题
19赞 Christian Loris 5/28/2013
格雷格,我回答的第一行是“这是对您问题的略微修改版本的回答。您的评论是多余的。
49赞 Rashmi Pandit 8/6/2009 #7

存储过程是只检索数据还是也修改数据?如果它仅用于检索,则可以将存储过程转换为函数,并使用公用表表达式 (CTE),而无需声明它,如下所示:

with temp as (
    select * from dbo.fnFunctionName(10, 20)
)
select col1, col2 from temp

但是,需要从 CTE 中检索的任何内容都应仅在一个语句中使用。你不能做一个,并在几行SQL之后尝试使用它。对于更复杂的查询,您可以在一个语句中包含多个 CTE。with temp as ...

例如

with temp1020 as (
    select id from dbo.fnFunctionName(10, 20)
),
temp2030 as (
    select id from dbo.fnFunctionName(20, 30)
)
select * from temp1020 
where id not in (select id from temp2030)

评论

1赞 yucer 12/3/2014
这些不是临时表,而是 CTE。technet.microsoft.com/en-us/library/......
6赞 Rashmi Pandit 12/5/2014
谢谢@yucer ...我相信我当时并不知道它们被称为 CTE:)
11赞 kevchadders 8/6/2009 #8

我发现了将数组/数据表传递到存储过程中,这可能会给你另一个关于如何解决问题的想法。

该链接建议使用 Image 类型参数传递到存储过程中。然后,在存储过程中,图像被转换为包含原始数据的表变量。

也许有一种方法可以将其与临时表一起使用。

评论

4赞 EndlessSpace 6/11/2011
在 Sql2008 及更高版本中,随着表值参数的引入,不再需要这样做。现在,您可以直接将 .net 数据集或数据表对象传递给 sql 存储过程,而必须按照上面的链接中所述将 .net 数据集或数据表对象转换为字节
9赞 pierre 8/11/2009 #9

另一种方法是创建一个类型并使用 PIPELINED 传回对象。但是,这仅限于了解列。但它的优点是能够做到:

SELECT * 
FROM TABLE(CAST(f$my_functions('8028767') AS my_tab_type))

评论

1赞 Martin Smith 1/11/2020
这是怎麽?似乎与这个问题所涉及的SQL Server没有任何关系
34赞 Doug Lubey of Louisiana 2/12/2010 #10

Quassnoi 把我大部分时间都放在那里,但缺少一件事:

我需要在存储过程中使用参数。

而 OPENQUERY 不允许这种情况发生:

因此,我找到了一种方法来工作系统,并且不必使表定义如此僵化,并在另一个存储过程中重新定义它(当然,它可能会破坏它)!

是的,您可以通过以下方式动态创建从存储过程返回的表定义 将 OPENQUERY 语句与虚假的可变量一起使用(只要 NO RESULT SET 返回 字段数量与具有良好数据的数据集相同,位置相同)。

创建表后,可以整天将 exec 存储过程使用到临时表中。


请注意(如上所述),您必须启用数据访问,

EXEC sp_serveroption 'MYSERVERNAME', 'DATA ACCESS', TRUE

法典:

declare @locCompanyId varchar(8)
declare @locDateOne datetime
declare @locDateTwo datetime

set @locDateOne = '2/11/2010'
set @locDateTwo = getdate()

--Build temporary table (based on bogus variable values)
--because we just want the table definition and
--since openquery does not allow variable definitions...
--I am going to use bogus variables to get the table defintion.

select * into #tempCoAttendanceRpt20100211
FROM OPENQUERY(DBASESERVER,
  'EXEC DATABASE.dbo.Proc_MyStoredProc 1,"2/1/2010","2/15/2010 3:00 pm"')

set @locCompanyId = '7753231'

insert into #tempCoAttendanceRpt20100211
EXEC DATABASE.dbo.Proc_MyStoredProc @locCompanyId,@locDateOne,@locDateTwo

set @locCompanyId = '9872231'

insert into #tempCoAttendanceRpt20100211
EXEC DATABASE.dbo.Proc_MyStoredProc @locCompanyId,@locDateOne,@locDateTwo

select * from #tempCoAttendanceRpt20100211
drop table #tempCoAttendanceRpt20100211

感谢您最初提供的信息...是的,最后,在使用来自 另一个存储过程或数据库,是的,您也可以使用参数。

搜索参考标签:

  • 将 SQL 2005 存储过程存储到临时表中

  • 使用存储过程和变量的 OpenQuery 2005

  • 带有变量的 OpenQuery

  • 将存储过程执行到临时表中

更新:这不适用于临时表,因此我不得不求助于手动创建临时表。

令人沮丧的是:这不适用于临时表http://www.sommarskog.se/share_data.html#OPENQUERY

参考:接下来是定义 LOCALSERVER。在示例中,它可能看起来像一个关键字,但实际上它只是一个名称。这是你如何做到的:

sp_addlinkedserver @server = 'LOCALSERVER',  @srvproduct = '',
                   @provider = 'SQLOLEDB', @datasrc = @@servername

若要创建链接服务器,必须具有 ALTER ANY SERVER 权限,或者是任何固定服务器角色 sysadmin 或 setupadmin 的成员。

OPENQUERY 将打开与 SQL Server 的新连接。这有一些含义:

使用 OPENQUERY 调用的过程不能引用在当前连接中创建的临时表。

新连接有自己的默认数据库(用 sp_addlinkedserver 定义,默认为 master),因此所有对象规范都必须包含数据库名称。

如果在调用 OPENQUERY 时有一个打开的事务并且持有锁,则调用的过程无法访问您锁定的内容。也就是说,如果你不小心,你会阻止自己。

连接不是免费的,因此会降低性能。

评论

1赞 Contango 7/1/2017
如果您不知道服务器名称,请使用 .您还可以使用SELECT @@SERVERNAMEEXEC sp_serveroption @@SERVERNAME, 'DATA ACCESS', TRUE
13赞 SoftwareARM 3/11/2011 #11

法典

CREATE TABLE #T1
(
    col1 INT NOT NULL,
    col2 NCHAR(50) NOT NULL,
    col3 TEXT NOT NULL,
    col4 DATETIME NULL,
    col5 NCHAR(50) NULL,
    col6 CHAR(2) NULL,
    col6 NCHAR(100) NULL,
    col7 INT NULL,
    col8 NCHAR(50) NULL,
    col9 DATETIME NULL,
    col10 DATETIME NULL
)

DECLARE @Para1 int
DECLARE @Para2 varchar(32)
DECLARE @Para3 varchar(100)
DECLARE @Para4 varchar(15)
DECLARE @Para5 varchar (12)
DECLARE @Para6 varchar(1)
DECLARE @Para7 varchar(1)


SET @Para1 = 1025
SET @Para2 = N'6as54fsd56f46sd4f65sd'
SET @Para3 = N'XXXX\UserName'
SET @Para4 = N'127.0.0.1'
SET @Para5 = N'XXXXXXX'
SET @Para6 = N'X'
SET @Para7 = N'X'

INSERT INTO #T1
(
    col1,
    col2,
    col3,
    col4,
    col5,
    col6,
    col6,
    col7,
    col8,
    col9,
    col10,
)
EXEC [dbo].[usp_ProcedureName] @Para1, @Para2, @Para3, @Para4, @Para5, @Para6, @Para6

我希望这会有所帮助。请酌情限定条件。

68赞 nitin 3/24/2011 #12
declare @temp table
(
    name varchar(255),
    field varchar(255),
    filename varchar(255),
    filegroup varchar(255),
    size varchar(255),
    maxsize varchar(255),
    growth varchar(255),
    usage varchar(255)
);
INSERT @temp  Exec sp_helpfile;
select * from @temp;

评论

6赞 t.durden 5/19/2017
没有解决 OP 原始问题,在没有先定义临时表的情况下进行插入。
0赞 TheRealChx101 11/9/2020
这正是我想要的。这允许我在无法控制过程时向结果集添加分页
24赞 satnhak 9/27/2011 #13

此存储过程可完成以下工作:

CREATE PROCEDURE [dbo].[ExecIntoTable]
(
    @tableName          NVARCHAR(256),
    @storedProcWithParameters   NVARCHAR(MAX)
)
AS
BEGIN
    DECLARE @driver         VARCHAR(10)
    DECLARE @connectionString   NVARCHAR(600)
    DECLARE @sql            NVARCHAR(MAX)
    DECLARE @rowsetSql      NVARCHAR(MAX)

    SET @driver = '''SQLNCLI'''

    SET @connectionString = 
        '''server=' + 
            CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(256)) + 
            COALESCE('\' + CAST(SERVERPROPERTY('InstanceName') AS NVARCHAR(256)), '') + 
        ';trusted_connection=yes'''

    SET @rowsetSql = '''EXEC ' + REPLACE(@storedProcWithParameters, '''', '''''') + ''''

    SET @sql = '
SELECT
    *
INTO 
    ' + @tableName + ' 
FROM
    OPENROWSET(' + @driver + ',' + @connectionString + ',' + @rowsetSql + ')'

    EXEC (@sql)
END
GO

对此略有修改:将存储过程结果插入表中,使其实际工作。

如果你想让它与临时表一起工作,那么你将需要使用一个表,然后删除它。##GLOBAL

56赞 FistOfFury 8/1/2012 #14

如果存储过程的结果表太复杂,无法手动键入“create table”语句,并且不能使用 OPENQUERY 或 OPENROWSET,则可以使用 sp_help 为您生成列和数据类型的列表。一旦你有了列列表,只需设置它的格式以满足你的需要。

第 1 步:将“into #temp”添加到输出查询中(例如“select [...]从 [...]“进入 #temp)。

最简单的方法是直接在 proc 中编辑输出查询。如果无法更改存储的过程,则可以将内容复制到新的查询窗口中,并在其中修改查询。

第 2 步:在临时表上运行 sp_help。(例如,“exec tempdb..sp_help #temp“)

创建临时表后,对临时表运行 sp_help 以获取列和数据类型的列表,包括 varchar 字段的大小。

步骤 3:将数据列和类型复制到 create table 语句中

我有一个 Excel 工作表,用于将sp_help的输出格式化为“创建表”语句。你不需要任何花哨的东西,只需复制并粘贴到你的SQL编辑器中。使用列名、大小和类型构造“Create table #x [...]”或“declare @x table [...]”语句,该语句可用于插入存储过程的结果。

第 4 步:插入到新创建的表格中

现在,你将有一个查询,该查询与此线程中描述的其他解决方案类似。

DECLARE @t TABLE 
(
   --these columns were copied from sp_help
   COL1 INT,
   COL2 INT   
)

INSERT INTO @t 
Exec spMyProc 

此技术还可用于将临时表 () 转换为表变量 ()。虽然这可能比自己编写语句要多一些步骤,但它可以防止大型流程中的手动错误,例如拼写错误和数据类型不匹配。调试拼写错误可能比首先编写查询花费更多时间。#temp@tempcreate table

18赞 Devansh 10/5/2013 #15
  1. 我正在创建一个包含以下架构和数据的表。

  2. 创建存储过程。

  3. 现在我知道我的过程的结果是什么,所以我正在执行以下查询。

     CREATE TABLE [dbo].[tblTestingTree](
         [Id] [int] IDENTITY(1,1) NOT NULL,
         [ParentId] [int] NULL,
         [IsLeft] [bit] NULL,
         [IsRight] [bit] NULL,
     CONSTRAINT [PK_tblTestingTree] PRIMARY KEY CLUSTERED
     (
         [Id] ASC
     ) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
     ) ON [PRIMARY]
     GO
     SET IDENTITY_INSERT [dbo].[tblTestingTree] ON
     INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (1, NULL, NULL, NULL)
     INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (2, 1, 1, NULL)
     INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (3, 1, NULL, 1)
     INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (4, 2, 1, NULL)
     INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (5, 2, NULL, 1)
     INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (6, 3, 1, NULL)
     INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (7, 3, NULL, 1)
     INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (8, 4, 1, NULL)
     INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (9, 4, NULL, 1)
     INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (10, 5, 1, NULL)
    
     SET IDENTITY_INSERT [dbo].[tblTestingTree] OFF
     VALUES (10, 5, 1, NULL)
     SET IDENTITY_INSERT [dbo].[tblTestingTree] On
    
    
     create procedure GetDate
     as
     begin
         select Id,ParentId from tblTestingTree
     end
    
     create table tbltemp
     (
         id int,
         ParentId int
     )
     insert into tbltemp
     exec GetDate
    
     select * from tbltemp;
    
48赞 StuartQ 2/6/2014 #16

如果 OPENROWSET 给您带来了问题,那么从 2012 年开始还有另一种方法;利用sys.dm_exec_describe_first_result_set_for_object,如下所述:检索存储过程的列名和类型?

首先,创建以下存储过程以生成临时表的 SQL:

CREATE PROCEDURE dbo.usp_GetStoredProcTableDefinition(
    @ProcedureName  nvarchar(128),
    @TableName      nvarchar(128),
    @SQL            nvarchar(max) OUTPUT
)
AS
SET @SQL = 'CREATE TABLE ' + @tableName + ' ('

SELECT @SQL = @SQL + '['+name +'] '+ system_type_name +''  + ','
        FROM sys.dm_exec_describe_first_result_set_for_object
        (
          OBJECT_ID(@ProcedureName), 
          NULL
        );

--Remove trailing comma
SET @SQL = SUBSTRING(@SQL,0,LEN(@SQL))    
SET @SQL =  @SQL +')'

若要使用该过程,请按以下方式调用它:

DECLARE     @SQL    NVARCHAR(MAX)

exec dbo.usp_GetStoredProcTableDefinition
    @ProcedureName='dbo.usp_YourProcedure',
    @TableName='##YourGlobalTempTable',@SQL = @SQL OUTPUT

INSERT INTO ##YourGlobalTempTable
EXEC    [dbo].usp_YourProcedure

select * from ##YourGlobalTempTable

请注意,我使用的是全局临时表。这是因为使用 EXEC 运行动态 SQL 会创建自己的会话,因此普通的临时表将超出任何后续代码的范围。如果全局临时表有问题,则可以使用普通的临时表,但任何后续 SQL 都需要是动态的,也就是说,也由 EXEC 语句执行。

评论

6赞 Trisped 7/7/2015
您忘了从 创建表。@SQL
0赞 Mohammadreza 2/23/2022
当我们在usp_YourProcedure中有临时表时,sys.dm_exec_describe_first_result_set_for_object给出错误:“无法确定元数据,因为语句......使用临时表”
10赞 zhongxiao37 8/18/2014 #17

我遇到了同样的问题,这是我根据保罗的建议为此所做的。这里最主要的部分是用来避免多个用户同时运行存储过程/脚本,为全局临时表带来的痛苦。NEWID()

DECLARE @sql varchar(max) = '', 
@tmp_global_table varchar(255) = '##global_tmp_' + CONVERT(varchar(36), NEWID())
SET @sql = @sql + 'select * into [' + @tmp_global_table + '] from YOURTABLE'
EXEC(@sql)

EXEC('SELECT * FROM [' + @tmp_global_table + ']')
-5赞 Hlin 8/20/2014 #18

我会做以下事情

  1. 创建(将 SP 转换为)UDF(表值 UDF)。

  2. select * into #tmpBusLine from dbo.UDF_getBusinessLineHistory '16 Mar 2009'

评论

2赞 yucer 12/7/2014
迈出第一步可能会遇到一些障碍。举例来说,如果原始 SP 使用临时表。UDF 不能使用临时表。
132赞 Tigerjz32 3/14/2015 #19

最简单的解决方案:

CREATE TABLE #temp (...);

INSERT INTO #temp
EXEC [sproc];

如果您不知道架构,则可以执行以下操作。请 请注意,此方法存在严重的安全风险。

SELECT * 
INTO #temp
FROM OPENROWSET('SQLNCLI', 
                'Server=localhost;Trusted_Connection=yes;', 
                'EXEC [db].[schema].[sproc]')

评论

0赞 SHEKHAR SHETE 7/6/2016
如果我不知道返回的结果集的列,那么???我的意思是列可能会有所不同。那么如何将结果插入临时表呢???
0赞 Tigerjz32 7/8/2016
您可以使用 OPENQUERY,但不建议这样做,因为它带有安全漏洞。
3赞 Adriaan Davel 7/20/2016
“如果我不知道返回的结果集的列”,那么你就不能在逻辑中使用它。如果您不知道数据是什么,您将如何使用这些数据?
0赞 Tigerjz32 8/17/2016
@AdriaanDavel 我同意你的观点,你应该始终知道你的数据(最佳实践),但是他可能说的是,有时 sproc 返回动态列,而你并不总是知道架构会是什么样子。在这种情况下,可以使用 OPENROWSET 动态插入和创建表。但是,这样做存在明显的安全风险......
1赞 Tigerjz32 10/31/2017
@nurettin有时您不知道存储过程将返回什么。在这种情况下会发生什么?如何创建临时表(当您不知道存储过程将返回什么时)并从存储过程插入其中?
4赞 Jayanthi Adilakshmi Visali 3/24/2015 #20

如果您知道要传递的参数,并且您无权进行sp_configure,则使用这些参数编辑存储过程,这些参数可以存储在 ##global 表中。

20赞 gotqn 6/3/2015 #21

若要将存储过程的第一个记录集插入到临时表中,需要了解以下内容:

  1. 只能将存储过程的第一行集插入到临时表中
  2. 存储过程不得执行动态 T-SQL 语句 (sp_executesql)
  3. 您需要先定义临时表的结构

以上可能看起来是限制,但恕我直言,它完全有意义 - 如果您正在使用,您可以返回两列和十列,如果您有多个结果集,则也不能将它们插入到多个表中 - 您可以在一个 T-SQL 语句的两个表中插入最大值(使用子句,没有触发器)。sp_executesqlOUTPUT

因此,问题主要是在执行语句之前如何定义临时表结构。EXEC ... INTO ...

第一个适用于,而第二个和第三个也适用于临时查询。我更喜欢使用 DMV 而不是 sp,因为您可以同时为多个过程使用和构建临时表定义。OBJECT_IDCROSS APPLY

SELECT p.name, r.* 
FROM sys.procedures AS p
CROSS APPLY sys.dm_exec_describe_first_result_set_for_object(p.object_id, 0) AS r;

另外,请注意该领域,因为它可能非常有用。它存储列的完整定义。例如:system_type_name

smalldatetime
nvarchar(max)
uniqueidentifier
nvarchar(1000)
real
smalldatetime
decimal(18,2)

在大多数情况下,您可以直接使用它来创建表定义。

因此,我认为在大多数情况下(如果存储过程符合某些条件),您可以轻松地构建动态语句来解决此类问题(创建临时表,在其中插入存储过程结果,对数据执行所需的操作)。


请注意,在某些情况下,上述对象无法定义第一个结果集数据,例如在执行动态 T-SQL 语句或在存储过程中使用临时表时。

评论

0赞 nahab 9/22/2016
对局限性的实际观察:如果您必须将某些 SP 的输出(我们称之为 SP_LEVEL_0)插入到另一个 SP 中使用上述方法动态创建的临时表(我们称之为 SP_LEVEL_1),则不能对将此SP_LEVEL_1输出到另一个临时表执行相同的技巧SP_LEVEL_2
16赞 ProblemSolver 9/7/2015 #22

如果查询不包含参数,请使用 else use .OpenQueryOpenRowset

基本的事情是按照存储过程创建架构并插入到该表中。例如:

DECLARE @abc TABLE(
                  RequisitionTypeSourceTypeID INT
                , RequisitionTypeID INT
                , RequisitionSourcingTypeID INT
                , AutoDistOverride INT
                , AllowManagerToWithdrawDistributedReq INT
                , ResumeRequired INT
                , WarnSupplierOnDNRReqSubmission  INT
                , MSPApprovalReqd INT
                , EnableMSPSupplierCounterOffer INT
                , RequireVendorToAcceptOffer INT
                , UseCertification INT
                , UseCompetency INT
                , RequireRequisitionTemplate INT
                , CreatedByID INT
                , CreatedDate DATE
                , ModifiedByID INT
                , ModifiedDate DATE
                , UseCandidateScheduledHours INT
                , WeekEndingDayOfWeekID INT
                , AllowAutoEnroll INT
                )
INSERT INTO @abc
EXEC [dbo].[usp_MySp] 726,3
SELECT * FROM @abc
30赞 Sandeep Gaadhe 2/26/2016 #23

如果您有幸拥有 SQL 2012 或更高版本,则可以使用dm_exec_describe_first_result_set_for_object

我刚刚编辑了 gotqn 提供的 sql。谢谢gotqn。

这将创建一个名称与过程名称相同的全局临时表。以后可以根据需要使用临时表。只是不要忘记在重新执行之前删除它。

    declare @procname nvarchar(255) = 'myProcedure',
            @sql nvarchar(max) 

    set @sql = 'create table ##' + @procname + ' ('
    begin
            select      @sql = @sql + '[' + r.name + '] ' +  r.system_type_name + ','
            from        sys.procedures AS p
            cross apply sys.dm_exec_describe_first_result_set_for_object(p.object_id, 0) AS r
            where       p.name = @procname

            set @sql = substring(@sql,1,len(@sql)-1) + ')'
            execute (@sql)
            execute('insert ##' + @procname + ' exec ' + @procname)
    end

评论

2赞 Gert Arnold 5/11/2018
非常好!只有一句话:如果要对内置存储过程执行此操作,请使用 instead 而不是。sys.all_objectssys.procedures
6赞 Trubs 6/20/2018
如果 SP 在其中使用临时表,此操作也会失败。(但将其作为武器库中的一个过程非常方便)
0赞 Even Mien 3/15/2023
这适用于 Azure SQL,而 OPENROWSET 则不然。
9赞 Matthew Baker 1/27/2017 #24

这可以在 SQL Server 2014+ 中完成,前提是存储过程仅返回一个表。如果有人找到一种为多个表执行此操作的方法,我很想知道。

DECLARE @storedProcname NVARCHAR(MAX) = ''
SET @storedProcname = 'myStoredProc'

DECLARE @strSQL AS VARCHAR(MAX) = 'CREATE TABLE myTableName '

SELECT @strSQL = @strSQL+STUFF((
SELECT ',' +name+' ' + system_type_name 
FROM sys.dm_exec_describe_first_result_set_for_object (OBJECT_ID(@storedProcname),0)
FOR XML PATH('')
),1,1,'(') + ')'

EXEC (@strSQL)

INSERT INTO myTableName

EXEC ('myStoredProc @param1=1, @param2=2')

SELECT * FROM myTableName

DROP TABLE myTableName

这将从系统表中提取返回表的定义,并使用它来为您构建临时表。然后,您可以如前所述从存储过程填充它。

还有一些变体也适用于动态 SQL。

评论

0赞 Suncat2000 4/2/2021
对于早期版本的 SQL Server,存储过程执行的操作几乎相同。有一篇博客文章与您的答案大致相同:使用临时表处理从另一个结果集创建表的sp_describe_first_result_set。然后插入到创建的表中。如果 MS 实施了 SELECT INTO 的 EXEC 版本,我们就不会有这种悲伤。谢谢你慢跑我的记忆!sp_describe_first_result_set
1赞 jmoreno 7/10/2018 #25

好吧,您确实必须创建一个临时表,但它不必具有正确的架构。我创建了一个存储过程,用于修改现有临时表,使其具有具有正确数据类型和顺序的必需列(删除所有现有列,添加新列):

GO
create procedure #TempTableForSP(@tableId int, @procedureId int)  
as   
begin  
    declare @tableName varchar(max) =  (select name  
                                        from tempdb.sys.tables 
                                        where object_id = @tableId
                                        );    
    declare @tsql nvarchar(max);    
    declare @tempId nvarchar(max) = newid();      
    set @tsql = '    
    declare @drop nvarchar(max) = (select  ''alter table tempdb.dbo.' + @tableName 
            +  ' drop column ''  + quotename(c.name) + '';''+ char(10)  
                                   from tempdb.sys.columns c   
                                   where c.object_id =  ' + 
                                         cast(@tableId as varchar(max)) + '  
                                   for xml path('''')  
                                  )    
    alter table tempdb.dbo.' + @tableName + ' add ' + QUOTENAME(@tempId) + ' int;
    exec sp_executeSQL @drop;    
    declare @add nvarchar(max) = (    
                                select ''alter table ' + @tableName 
                                      + ' add '' + name 
                                      + '' '' + system_type_name 
                           + case when d.is_nullable=1 then '' null '' else '''' end 
                                      + char(10)   
                              from sys.dm_exec_describe_first_result_set_for_object(' 
                               + cast(@procedureId as varchar(max)) + ', 0) d  
                                order by column_ordinal  
                                for xml path(''''))    

    execute sp_executeSQL  @add;    
    alter table '  + @tableName + ' drop column ' + quotename(@tempId) + '  ';      
    execute sp_executeSQL @tsql;  
end         
GO

create table #exampleTable (pk int);

declare @tableId int = object_Id('tempdb..#exampleTable')
declare @procedureId int = object_id('examplestoredProcedure')

exec #TempTableForSP @tableId, @procedureId;

insert into #exampleTable
exec examplestoredProcedure

请注意,如果sys.dm_exec_describe_first_result_set_for_object存储过程无法确定存储过程的结果(例如,如果它使用临时表),则此操作将不起作用。

6赞 S Krishna 4/27/2019 #26

这是一个简单的 2 步过程: - 创建临时表 - 插入到临时表中。

执行相同操作的代码:

CREATE TABLE #tempTable (Column1 int, Column2 varchar(max));
INSERT INTO #tempTable 
EXEC [app].[Sproc_name]
@param1 = 1,
@param2 =2;
8赞 vendettamit 6/14/2019 #27

在四处搜索之后,我找到了一种方法,可以为任何存储过程动态创建临时表,而无需使用或使用存储过程结果定义的通用架构,尤其是当您不是数据库管理员时。OPENROWSETOPENQUERY

Sql Server 有一个内置过程,可以为你提供任何过程结果集的架构。我根据此过程的结果创建了一个架构表,并手动将所有字段设置为 NULLABLE。sp_describe_first_result_set

declare @procname varchar(100) = 'PROCEDURENAME' -- your procedure name
declare @param varchar(max) = '''2019-06-06''' -- your parameters 
declare @execstr nvarchar(max) = N'exec ' + @procname
declare @qry nvarchar(max)

-- Schema table to store the result from sp_describe_first_result_set.
create table #d
(is_hidden  bit  NULL, column_ordinal   int  NULL, name sysname NULL, is_nullable   bit  NULL, system_type_id   int  NULL, system_type_name nvarchar(256) NULL,
max_length  smallint  NULL, precision   tinyint  NULL,  scale   tinyint  NULL,  collation_name  sysname NULL, user_type_id  int NULL, user_type_database    sysname NULL,
user_type_schema    sysname NULL,user_type_name sysname NULL,assembly_qualified_type_name   nvarchar(4000),xml_collection_id    int NULL,xml_collection_database    sysname NULL,
xml_collection_schema   sysname NULL,xml_collection_name    sysname NULL,is_xml_document    bit  NULL,is_case_sensitive bit  NULL,is_fixed_length_clr_type  bit  NULL,
source_server   sysname NULL,source_database    sysname NULL,source_schema  sysname NULL,source_table   sysname NULL,source_column  sysname NULL,is_identity_column bit NULL,
is_part_of_unique_key   bit NULL,is_updateable  bit NULL,is_computed_column bit NULL,is_sparse_column_set   bit NULL,ordinal_in_order_by_list   smallint NULL,
order_by_list_length    smallint NULL,order_by_is_descending    smallint NULL,tds_type_id   int  NULL,tds_length    int  NULL,tds_collation_id  int NULL,
tds_collation_sort_id   tinyint NULL)


-- Get result set definition of your procedure
insert into #d
EXEC sp_describe_first_result_set @exestr, NULL, 0

-- Create a query to generate and populate a global temp table from above results
select 
@qry = 'Create table ##t(' +
stuff(  
    (select ',' + name + ' '+ system_type_name + ' NULL'
    from #d d For XML Path, TYPE)
    .value(N'.[1]', N'nvarchar(max)')
, 1,1,'')
+ ')

insert into ##t 
Exec '+@procname+' ' + @param

Exec sp_executesql @qry

-- Use below global temp table to query the data as you may
select * from ##t

-- **WARNING** Don't forget to drop the global temp table ##t.
--drop table ##t
drop table #d 

在 SQL Server 版本 - Microsoft SQL Server 2016 (RTM) - 13.0.1601.5(内部版本 17134:) 上开发和测试

您可以调整您正在使用的 SQL Server 版本的架构(如果需要)。

3赞 Charles Byrne 9/21/2019 #28

这个问题晚了几年,但我需要这样的东西来快速生成一些肮脏的代码。我相信正如其他人所说,预先定义临时表更容易,但这种方法应该适用于简单的存储过程查询或 sql 语句。

这有点复杂,但它借鉴了这里的贡献者以及 Paul White 从 DBA Stack Exchange Get 存储过程结果列类型的解决方案。同样,重申这种方法和示例不是为多用户环境中的流程而设计的。在这种情况下,表定义在全局临时表中设置了一小段时间,以供代码生成模板进程引用。

我还没有完全测试过这一点,所以可能有一些警告,所以你可能想转到保罗怀特的答案中的MSDN链接。这适用于 SQL 2012 及更高版本。

首先使用类似于 Oracle 描述的存储过程sp_describe_first_result_set

这将计算第一个结果集的第一行,因此,如果存储过程或语句返回多个查询,它将仅描述第一个结果。

我创建了一个存储过程来分解返回单个字段以从中选择以创建临时表定义的任务。

CREATE OR ALTER PROCEDURE [dbo].[sp_GetTableDefinitionFromSqlBatch_DescribeFirstResultSet]
(
     @sql NVARCHAR(4000)
    ,@table_name VARCHAR(100)
    ,@TableDefinition NVARCHAR(MAX) OUTPUT
)
AS
BEGIN
    SET NOCOUNT ON
    DECLARE @TempTableDefinition NVARCHAR(MAX)
    DECLARE @NewLine NVARCHAR(4) = CHAR(13)+CHAR(10)

    DECLARE @ResultDefinition TABLE (  --The View Definition per MSDN
      is_hidden         bit NOT NULL
    , column_ordinal    int NOT NULL
    , [name]            sysname NULL
    , is_nullable       bit NOT NULL
    , system_type_id    int NOT NULL
    , system_type_name  nvarchar(256) NULL
    , max_length        smallint NOT NULL
    , [precision]       tinyint NOT NULL
    , scale             tinyint NOT NULL
    , collation_name    sysname NULL    
    , user_type_id      int NULL
    , user_type_database    sysname NULL    
    , user_type_schema  sysname NULL
    , user_type_name    sysname NULL    
    , assembly_qualified_type_name      nvarchar(4000)  
    , xml_collection_id         int NULL
    , xml_collection_database   sysname NULL    
    , xml_collection_schema     sysname NULL    
    , xml_collection_name       sysname NULL
    , is_xml_document           bit NOT NULL            
    , is_case_sensitive         bit NOT NULL            
    , is_fixed_length_clr_type  bit NOT NULL    
    , source_server             sysname NULL            
    , source_database           sysname NULL
    , source_schema             sysname NULL
    , source_table              sysname NULL
    , source_column             sysname NULL
    , is_identity_column        bit NULL
    , is_part_of_unique_key     bit NULL
    , is_updateable             bit NULL
    , is_computed_column        bit NULL
    , is_sparse_column_set      bit NULL
    , ordinal_in_order_by_list  smallint NULL   
    , order_by_is_descending    smallint NULL   
    , order_by_list_length      smallint NULL
    , tds_type_id               int NOT NULL
    , tds_length                int NOT NULL
    , tds_collation_id          int NULL
    , tds_collation_sort_id     tinyint NULL
    )

    --Insert the description into table variable    
    INSERT @ResultDefinition
    EXEC sp_describe_first_result_set @sql

    --Now Build the string to create the table via union select statement
    ;WITH STMT AS (
        SELECT N'CREATE TABLE ' + @table_name + N' (' AS TextVal
        UNION ALL

        SELECT 
         CONCAT(
                CASE column_ordinal
                    WHEN 1 THEN '     ' ELSE '   , ' END  --Determines if comma should precede
                , QUOTENAME([name]) , '   ', system_type_name  -- Column Name and SQL TYPE
                ,CASE is_nullable 
                    WHEN 0 THEN '   NOT NULL' ELSE '   NULL' END --NULLABLE CONSTRAINT          
               ) AS TextVal
        FROM @ResultDefinition WHERE is_hidden = 0  -- May not be needed
        UNION ALL

        SELECT N');' + @NewLine
    ) 

    --Now Combine the rows to a single String
    SELECT @TempTableDefinition = COALESCE (@TempTableDefinition + @NewLine + TextVal, TextVal) FROM STMT

    SELECT @TableDefinition = @TempTableDefinition
END

难题是你需要使用一个全局表,但你需要使它足够独特 因此,您可以经常从中拖放和创建,而不必担心碰撞。
在此示例中,我使用了 Guid (FE264BF5_9C32_438F_8462_8A5DC8DEE49E) 作为全局变量,将连字符替换为下划线

DECLARE @sql NVARCHAR(4000) = N'SELECT @@SERVERNAME as ServerName, GETDATE() AS Today;'
DECLARE @GlobalTempTable VARCHAR(100) = N'##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable'

--@sql can be a stored procedure name like dbo.foo without parameters

DECLARE @TableDef NVARCHAR(MAX)

DROP TABLE IF EXISTS #MyTempTable
DROP TABLE IF EXISTS ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable

EXEC [dbo].[sp_GetTableDefinitionFromSqlBatch_DescribeFirstResultSet] 
    @sql, @GlobalTempTable, @TableDef OUTPUT

--Creates the global table ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable
EXEC sp_executesql @TableDef 

--Now Call the stored procedure, SQL Statement with Params etc.
INSERT ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable
    EXEC sp_executesql @sql 

--Select the results into your undefined Temp Table from the Global Table
SELECT * 
INTO #MyTempTable
FROM ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable

SELECT * FROM #MyTempTable

DROP TABLE IF EXISTS #MyTempTable
DROP TABLE IF EXISTS ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable

同样,我只使用简单的存储过程查询和简单的查询对其进行了测试,因此您的里程可能会有所不同。希望这对某人有所帮助。

2赞 Ludovic Aubert 11/30/2019 #29

如果让动态 SQL 创建临时表,则此表由动态 SQL 连接拥有,而不是从中调用存储过程的连接。

DECLARE @COMMA_SEPARATED_KEYS varchar(MAX);
DROP TABLE IF EXISTS KV;
CREATE TABLE KV (id_person int, mykey varchar(30), myvalue int);
INSERT INTO KV VALUES
(1, 'age', 16),
(1, 'weight', 63),
(1, 'height', 175),
(2, 'age', 26),
(2, 'weight', 83),
(2, 'height', 185);
WITH cte(mykey) AS (
    SELECT DISTINCT mykey FROM KV
) 
SELECT @COMMA_SEPARATED_KEYS=STRING_AGG(mykey,',') FROM cte;
SELECT @COMMA_SEPARATED_KEYS AS keys;

enter image description here

DECLARE @ExecuteExpression varchar(MAX);

DROP TABLE IF EXISTS #Pivoted;

SET @ExecuteExpression = N'
SELECT * 
INTO #Pivoted
FROM
(
    SELECT
        mykey,
        myvalue,
        id_person
    FROM KV
) AS t
PIVOT(
    MAX(t.myvalue) 
    FOR mykey IN (COMMA_SEPARATED_KEYS)
) AS pivot_table;
';

SET @ExecuteExpression = REPLACE(@ExecuteExpression, 'COMMA_SEPARATED_KEYS', @COMMA_SEPARATED_KEYS);

EXEC(@ExecuteExpression);

SELECT * FROM #Pivoted;

消息 208,级别 16,状态 0 对象名称“#Pivoted”无效。 这是因为 #Pivoted 由动态 SQL 连接拥有。所以最后一条指令

SELECT * FROM #Pivoted

失败。

不遇到此问题的一种方法是确保所有对 #Pivoted 的引用都是从动态查询本身内部进行的:

DECLARE @COMMA_SEPARATED_KEYS varchar(MAX);
DROP TABLE IF EXISTS KV;
CREATE TABLE KV (id_person int, mykey varchar(30), myvalue int);
INSERT INTO KV VALUES
(1, 'age', 16),
(1, 'weight', 63),
(1, 'height', 175),
(2, 'age', 26),
(2, 'weight', 83),
(2, 'height', 185);
WITH cte(mykey) AS (
    SELECT DISTINCT mykey FROM KV
) 
SELECT @COMMA_SEPARATED_KEYS=STRING_AGG(mykey,',') FROM cte;
SELECT @COMMA_SEPARATED_KEYS AS keys;


DECLARE @ExecuteExpression varchar(MAX);

DROP TABLE IF EXISTS #Pivoted;

SET @ExecuteExpression = N'
SELECT * 
INTO #Pivoted
FROM
(
    SELECT
        mykey,
        myvalue,
        id_person
    FROM KV
) AS t
PIVOT(
    MAX(t.myvalue) 
    FOR mykey IN (COMMA_SEPARATED_KEYS)
) AS pivot_table;
SELECT * FROM #Pivoted;
';

SET @ExecuteExpression = REPLACE(@ExecuteExpression, 'COMMA_SEPARATED_KEYS', @COMMA_SEPARATED_KEYS);

EXEC(@ExecuteExpression);

enter image description here

评论

0赞 user2864740 2/17/2021
它是相同的“连接”和不同的“作用域”(就像调用了定义本地临时表的嵌套 SP 一样)。
0赞 JeffO 3/15/2022
在动态查询中创建一个全局临时表 ##Pivoted,然后可以从外部访问它。
3赞 Valentin Petkov 6/15/2020 #30

这是我的带有参数的 T-SQL

--require one time execution if not configured before
sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO

--require one time execution if not configured before
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO


--the query
DECLARE @param1 int = 1, @param2 int = 2
DECLARE @SQLStr varchar(max) = 'SELECT * INTO #MyTempTable
                                FROM OPENROWSET(''SQLNCLI'',  
''Server=ServerName;Database=DbName;Trusted_Connection=yes'',
''exec StoredProcedureName '+ CAST(@param1 AS varchar(15)) +','+ CAST(@param2 AS varchar(15)) +''') AS a ;
 select * from #MyTempTable;
 drop table #MyTempTable        
';
EXECUTE(@SQLStr);
0赞 BenW 6/18/2021 #31

首先,修改存储过程以将最终结果保存到临时表中。通过这样做,我们正在创建一个与 SP 输出字段匹配的表。然后使用 select 语句将该临时表保存为任何表名。然后按照步骤 2 中的说明执行 SP

步骤 1:修改存储过程以将最终结果保存到临时表

[your stored procedure] 

into #table_temp //this will insert the data to a temp table

from  #table_temp 

select * into SP_Output_Table_1 from #table_temp //this will save data to a actual table

第 2 步:执行如下所示的 SP,将记录插入到您的表中

Insert SP_Output_Table_1
EXE  You_SP_Nane @Parameter1 = 52, @parameter2 =1

评论

0赞 Smandoli 12/4/2021
与@dotjoe stackoverflow.com/a/654410/122139 基本相同;但是,这两个答案一起最适合提供细节和清晰度。
-3赞 Stilero 2/14/2022 #32
  1. 首先在模板中选择一个空结果集,以便创建它。
  2. 将 exec SP 运行到 TABLE 中
SELECT TOP 0 * INTO [temp_table] FROM [Table]; 

EXEC [StoredProcedure] INTO [temp_table];

评论

0赞 Paul-Sebastian Manole 1/12/2023
这不是有效的 T-SQL!SQL Server 仅支持语法。INSERT INTO ... EXECUTE ...
1赞 Eddie Kumar 12/30/2022 #33

旧帖子但有用。 除了其他答案之外,还可以将存储过程的输出捕获到表变量中(因为提问者希望避免使用 #temp 表),因此我使用@table变量创建了一个完全有效的示例:

--Note: separately run each section at a time:
--Section-1: create stored proc that outputs a result-set:
    CREATE OR ALTER PROCEDURE TestSP AS
    BEGIN
        SELECT database_id, name from sys.databases
    END
------------------------
--Section-2: create @table-variable with columns matching the output (alternatively #Temp table can be used):
    DECLARE @t TABLE (did INT, dname VARCHAR(99))

    --Capture the output:
    insert into @t
    exec TestSP

    --View the captured output:
    select * from @t
------------------------
--Section-3: Clean-up
    DROP PROCEDURE TestSP

HTH。

评论

0赞 Paul-Sebastian Manole 1/12/2023
可能只插入第一个结果集,对吧?还是只是最后一个?还是尝试从每个结果集中插入?
1赞 Eddie Kumar 1/31/2023
@Paul-SebastianManole,如果存储的过程返回多个结果集,如果所有结果集的列匹配(相同数量的列、数据类型等),则所有结果集的输出将被合并(将使用最后一个结果集的列标题),但是,如果列不匹配,则尝试将多个结果集的输出存储在表变量中将产生以下错误: “列名或提供的值数与表定义不匹配”。遗憾的是,SQL Server 不提供仅捕获其中一个输出/结果集的方法。HTH。
0赞 Monzur 8/19/2023
我不知道为什么,不在这里工作......
0赞 Eddie Kumar 8/21/2023
@Monzur,什么不起作用,你遇到了什么错误?
0赞 Hugo Bounoua 3/31/2023 #34

最简单的解决方案: 如果您可以稍微修改 SP。

我想分享这个,因为我尝试了上述所有方法,但出于安全原因没有任何效果,所以我最终做了这个非常简单的修改。

无论您的 SP 返回什么,INSERT 都会在带有 ## 的临时表中产生这些结果(## 使其成为全局临时,# 将使其仅本地) 因此,在 SP 结束时,当生成结果时,转换:

选择。。。;

由:

插入 ##temp;

选择。。。;

从 ##temp; 中选择 *

现在,无论您想在哪个脚本中使用结果,只需在执行后使用 ##temp 作为常规表

执行...;