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

时间:2009-03-17 10:45:26

标签: sql sql-server sql-server-2005 tsql stored-procedures

我如何做SELECT * INTO [temp table] FROM [stored procedure]?不是FROM [Table]而没有定义[temp table]

SelectBusinessLinetmpBusLine的所有数据都可以正常工作。

select *
into tmpBusLine
from BusinessLine

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

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

输出消息:

  

第15行,第1行,第2行   关键字附近的语法不正确   'EXEC'。

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

31 个答案:

答案 0 :(得分:669)

您可以使用OPENROWSET。看一看。我还包括sp_configure代码以启用Ad Hoc Distributed Queries,以防它尚未启用。

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

答案 1 :(得分:561)

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

CREATE TABLE #tmpBus
(
   COL1 INT,
   COL2 INT
)

INSERT INTO #tmpBus
Exec SpGetRecords 'Params'

答案 2 :(得分:278)

在SQL Server 2005中,您可以使用INSERT INTO ... EXEC将存储过程的结果插入表中。从MSDN's INSERT documentation(对于SQL Server 2000,实际上):

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

答案 3 :(得分:183)

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

这里有一个很好的quick article和其他用户定义的函数。如果仍然需要存储过程,则可以使用存储过程包装内联表值用户定义函数。当存储过程从内联表值用户定义函数调用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数据源。这个   method是访问的替代方法   链接服务器中的表是一个   一次性,特殊的连接方法   和使用OLE访问远程数据   D B。对于更频繁的引用   OLE DB数据源,使用链接   而不是服务器。

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

答案 4 :(得分:120)

EXEC sp_serveroption 'YOURSERVERNAME', 'DATA ACCESS', TRUE

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

答案 5 :(得分:114)

  

最简单的解决方案:

CREATE TABLE #temp (...);

INSERT INTO #temp
EXEC [sproc];

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

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

答案 6 :(得分:96)

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

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

答案 7 :(得分:63)

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;

答案 8 :(得分:45)

您的存储过程是否仅检索数据或修改数据?如果它仅用于检索,则可以将存储过程转换为函数并使用公用表表达式(CTE)而不必声明它,如下所示:

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

但是,无论需要从CTE检索什么,都只能在一个声明中使用。您不能执行with temp as ...并尝试在几行SQL之后使用它。您可以在一个语句中包含多个CTE,以进行更复杂的查询。

例如,

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)

答案 9 :(得分:45)

如果存储过程的结果表太复杂而无法手动输入“create table”语句,并且您无法使用OPENQUERY OR OPENROWSET,则可以使用sp_help生成列和数据类型列表您。获得列列表后,只需格式化它就可以满足您的需求。

步骤1:将“输入#temp”添加到输出查询中(例如“从[...]中选择[...]到#temp”。)

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

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

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

第3步:复制数据列&类型为创建表语句

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

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

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

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

INSERT INTO @t 
Exec spMyProc 

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

答案 10 :(得分:29)

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 with stored procedure and variables 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时持有锁,则被调用的过程无法访问您锁定的内容。也就是说,如果你不小心,你会阻止自己。

连接不是免费的,因此会有性能损失。

答案 11 :(得分:26)

如果OPENROWSET导致您出现问题,那么从2012年开始还有另一种方式;使用sys.dm_exec_describe_first_result_set_for_object,如下所述:Retrieve column names and types of a stored procedure?

首先,创建此存储过程以生成临时

的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语句执行的。

答案 12 :(得分:21)

这个存储过程完成了这项工作:

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

这是对此的轻微修改:Insert stored procedure results into table以便它确实有效。

如果您希望它与临时表一起使用,那么您将需要使用##GLOBAL表并在之后删除它。

答案 13 :(得分:18)

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

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

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

    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

答案 14 :(得分:16)

  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;
    

答案 15 :(得分:15)

为了将存储过程的第一个记录集插入临时表,您需要知道以下内容:

  1. 只能将存储过程的第一行集插入临时表
  2. 存储过程不得执行动态T-SQL语句(sp_executesql
  3. 您需要先定义临时表的结构
  4. 以上可能看起来有限制,但恕我直言它完全有意义 - 如果你使用sp_executesql,你可以一次返回两列,一次十,如果你有多个结果集,你不能将它们插入几个表也​​是 - 您可以在一个T-SQL语句中使用两个表插入最大值(使用OUTPUT子句而不使用触发器。)

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

    第一个适用于OBJECT_ID,而第二个和第三个适用于Ad-hoc查询。我更喜欢使用DMV而不是sp,因为您可以使用CROSS 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)
    

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

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

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

答案 16 :(得分:14)

如果查询不包含参数,请使用OpenQuery否则使用OpenRowset

基本的做法是根据存储过程创建模式并插入到该表中。 e.g:

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

答案 17 :(得分: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

我希望这会有所帮助。请酌情符合资格。

答案 18 :(得分:11)

我找到了 Passing Arrays/DataTables into Stored Procedures ,这可能会让您对如何解决问题有所了解。

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

也许有一种方法可以用于临时表。

答案 19 :(得分:9)

我遇到了同样的问题,这就是我为此Paul's suggestion所做的。这里的主要部分是使用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 + ']')

答案 20 :(得分:8)

另一种方法是创建一个类型并使用PIPELINED然后传回你的对象。但这仅限于知道列。但它具有能够做到的优势:

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

答案 21 :(得分:3)

如果您知道正在传递的参数,并且您无权访问make sp_configure,那么使用这些参数编辑存储过程,并将其存储在##全局表中。

答案 22 :(得分:3)

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

执行相同操作的代码:

CREATE TABLE #tempTable (Column1 int, Column2 varchar(max));
INSERT INTO #tempTable 
EXEC [app].[Sproc_name]
@param1 = 1,
@param2 =2;

答案 23 :(得分:3)

经过搜索后,我发现了一种使用存储过程结果定义的通用模式而不使用OPENROWSETOPENQUERY动态地为任何存储过程创建临时表的方法,尤其是当您不是数据库管理员时。

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

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版本调整架构(如果需要)。

答案 24 :(得分:2)

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

sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO

sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO

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);

答案 25 :(得分:1)

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

这有点令人费解,但是它借鉴了这里的贡献者以及DBA Stack Exchange Get stored procedure result column-types的Paul White的解决方案。再次重申,此方法和示例并非为多用户环境中的流程而设计。在这种情况下,表定义将在全局临时表中设置一小段时间,以供代码生成模板过程参考。

我尚未对此进行全面测试,因此可能存在一些警告,因此您可能需要转到Paul White答案中的MSDN链接。这适用于SQL 2012及更高版本。

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

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

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

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

同样,我仅通过简单的存储过程查询和简单查询对它进行了测试,因此您的工作量可能会有所不同。希望这对某人有帮助。

答案 26 :(得分:0)

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

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无法确定存储过程的结果(例如,如果使用临时表),则此方法将无效。

答案 27 :(得分:0)

如果让动态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

答案 28 :(得分:0)

首先,修改您的存储过程以将最终结果保存到临时表中。通过这样做,我们正在创建一个与 SP 输出字段匹配的表。然后有一个选择语句将该临时表保存为任何表名。然后按照步骤 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

答案 29 :(得分:-1)

这可以在SQL Server 2014+中完成,前提是SP只返回一个表。如果有人为多个表找到了这样做的方法,我很乐意了解它。

DECLARE @storeProcname NVARCHAR(MAX) = ''

SET @storeProcname = '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(@storeProcname),0)
FOR XML PATH('')
),1,1,'(') + ')'

EXEC (@strSQL)

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

SELECT * FROM myTableName

DROP TABLE myTableName

这将从系统表中提取返回表的定义,并使用它来为您构建临时表。然后,您可以按照前面的说明从SP填充它。

此外,还有一些变体可以与Dynamic SQL一起使用。

答案 30 :(得分:-4)

我会做以下

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

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