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

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

BusinessLine中select所有数据到tmpBusLine工作正常。

 select * into tmpBusLine from BusinessLine 

我正在尝试相同的,但使用返回数据的存储过程,是不完全相同的。

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

输出消息:

Msg 156,Level 15,State 1,Line 2关键字'exec'附近的语法不正确。

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

你可以使用OPENROWSET 。 看一看。 如果还没有启用,我还包含sp_configure代码以启用Ad Hoc分布式查询。

 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 

如果你不想先声明临时表,就可以尝试创build一个用户定义的函数,而不是存储过程 ,并使该用户定义的函数返回一个表。 另外,如果你想使用存储过程,请尝试如下所示:

 CREATE TABLE #tmpBus ( COL1 INT, COL2 INT ) INSERT INTO #tmpBus Exec SpGetRecords 'Params' 

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

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

这是你的问题的一个稍微修改版本的答案。 如果您可以放弃使用用户定义函数的存储过程,则可以使用内联表值用户定义的函数。 这本质上是一个存储过程(将采取参数),作为结果集返回一个表; 因此将很好地与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 

我认为这是获得预期结果的最“无用”方法。 它使用现有的function,因为他们打算使用,没有其他复杂的。 通过将embedded式表值用户定义的函数嵌套在存储过程中,您可以通过两种方式访问​​该function。 加! 实际的SQL代码只有一个维护点。

有人build议使用OPENROWSET,但这不是OPENROWSET函数的目的(来自联机丛书):

包括从OLE DB数据源访问远程数据所需的所有连接信息。 此方法是访问链接服务器中的表的替代方法,它是使用OLE DB连接和访问远程数据的一次性临时方法。 要更频繁地引用OLE DB数据源,请改为使用链接的服务器。

使用OPENROWSET将完成工作,但是会导致打开本地连接和编组数据的额外开销。 在所有情况下,它也可能不是一种select,因为它需要临时查询许可,这会造成安全风险,因此可能不被期望。 另外,OPENROWSET方法将阻止使用返回多个结果集的存储过程。 在单个存储过程中包装多个行内表值用户定义的函数可以实现这一点。

 SELECT * INTO #tmpTable FROM OPENQUERY(YOURSERVERNAME, 'EXEC test.dbo.prc_test 1') 

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

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

最简单的解决scheme

 CREATE TABLE #temp (...); INSERT INTO #temp EXEC [sproc]; 

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

 SELECT * INTO #temp FROM OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=yes;', 'EXEC [db].[schema].[sproc]') 
 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; 

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

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

但是,CTE的任何需求只能在一个陈述中使用。 你不能像with temp as ...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) 

如果存储过程的结果表太复杂,无法手工input“create table”语句,也无法使用OPENQUERY或OPENROWSET,则可以使用sp_help为您生成列和数据types列表。 一旦你有列的列表,这只是一个格式化,以满足您的需求的问题。

步骤1:将“input到#temp”添加到输出查询中(例如“从[…]中select到#temp中”)。

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

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

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

第3步:将数据列和types复制到create table语句中

我有一个Excel工作表,我用它来将sp_help的输出格式化为一个“create table”语句。 你不需要任何奇怪的东西,只需复制并粘贴到你的SQL编辑器。 使用列名称,大小和types来构造“Create table #x […]”或“declare @x table […]”语句,您可以使用它来插入存储过程的结果。

第四步:插入新创build的表格

现在,您将有一个类似于此主题中描述的其他解决scheme的查询。

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

这种技术也可以用来将临时表( #temp )转换为表variables( @temp )。 虽然这可能比仅仅编写create table语句更多的步骤,但它可以防止大进程中的手工错误,如拼写错误和数据types不匹配。 debugging拼写错误可能比首先编写查询花费更多的时间。

Quassnoi把我的大部分路都带到了那里,但是有一件事情不见了:

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

OPENQUERY不允许发生这种情况:

所以我find了一个工作系统的方法,而且不必使表定义如此僵化,并在另一个存储过程中重新定义它(当然也有可能中断它)!

是的,您可以通过使用伪造可变参数的OPENQUERY语句(只要NO RESULT SET返回与具有良好数据的数据集相同数量的字段和相同位置)来dynamic创build从存储过程返回的表定义。

一旦表被创build,你可以整天使用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 

感谢您提供最初提供的信息… 是的,最后我不必在使用其他存储过程或数据库中的数据时创build所有这些伪造的 (严格的)表格定义,而且您也可以使用参数。

search参考标签:

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

  • 使用存储过程和variables的openquery 2005

  • 与variables的openquery

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

更新: 这将无法与临时表,所以我不得不求助于手动创build临时表。

无赖的通知 :这不会与临时表http://www.sommarskog.se/share_data.html#OPENQUERY

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

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

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

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

您使用OPENQUERY调用的过程无法引用在当前连接中创build的临时表。

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

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

连接不是免费的,所以会有性能损失。

如果OPENROWSET正在引起你的问题,那么从2012年开始还有另一种方法。 使用sys.dm_exec_describe_first_result_set_for_object,如下所述: 检索存储过程的列名和types?

首先,创build这个存储过程来为临时生成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运行dynamicSQL会创build自己的会话,因此普通的临时表将超出任何后续代码的范围。 如果全局临时表是一个问题,那么可以使用一个普通的临时表,但是任何后续的SQL都需要是dynamic的,也就是说,EXEC语句也会执行这个表。

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

 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表,然后放下它。

为了将stored procedure的第一个record set插入temporary table您需要知道以下内容:

  1. 只有存储过程的第一行可以插入临时表中
  2. 存储过程不得执行dynamicT-SQL语句( sp_executesql
  3. 你需要先定义临时表的结构

上面可能看起来是有限制的,但恕我直言,它是完全有道理的 – 如果你正在使用sp_executesql你可以一次返回两列,一次十,如果你有多个结果集,你不能插入几个表 – 你可以插入在一个T-SQL语句中使用两个表中的最大值(使用OUTPUT子句并且没有触发器)。

所以,问题主要是如何在执行EXEC ... INTO ...语句之前定义temporary table结构。

  • sys.dm_exec_describe_first_result_set_for_object
  • sys.dm_exec_describe_first_result_set
  • sp_describe_first_result_set

第一个使用OBJECT_ID而第二个和第三个使用Ad-hoc查询。 我更喜欢使用DMV代替sp因为您可以使用CROSS APPLY并同时为多个过程构buildtemporary table definitions

 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) 

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

所以,我认为在大多数情况下(如果存储过程符合某些标准),您可以轻松地构build一个dynamic语句来解决这些问题(创build临时表,将存储过程结果插入到临时表中,根据数据执行所需操作)。


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

 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 

我希望这有帮助。 请适当的资格。

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

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

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

  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 = 'myProcedure' set @sql = substring(@sql,1,len(@sql)-1) + ')' execute (@sql) execute('insert ##' + @procname + ' exec ' + @procname) end 

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

链接build议使用Imagetypes参数传入存储过程。 然后在存储过程中,将图像转换为包含原始数据的表variables。

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

  1. 我用下面的模式和数据创build一个表。
  2. 创build一个存储过程。
  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 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 

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

基本的东西是按照存储过程创build模式并插入到表中。 例如:

 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 

另一种方法是创build一个types并使用PIPELINED来传回对象。 然而这仅限于了解列。 但它具有可以做的优点:

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

我遇到了同样的问题, 保罗的build议就是这样做的 。 The main part is here is to use NEWID() to avoid multiple users run the store procedures/scripts at the same time, the pain for global temporary table.

 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 + ']') 

If you know the parameters that are being passed and if you don't have access to make sp_configure, then edit the stored procedure with these parameters and the same can be stored in a ##global table.

This can be done in SQL Server 2014+ provided SP only returns one table. If anyone finds a way of doing this for multiple tables I'd love to know about it.

 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 

This pulls the definition of the returned table from system tables, and uses that to build the temp table for you. You can then populate it from the SP as stated before.

There are also variants of this that work with Dynamic SQL too.

I would do the following

  1. Create (convert SP to) a UDF (Table value UDF).

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