我该怎么办?不是SELECT * INTO [temp table] FROM [stored procedure]且未定义FROM [Table]吗?

[temp table]SelectBusinessLine的所有数据都可以正常工作。

select *
into tmpBusLine
from BusinessLine


我正在尝试相同,但使用返回数据的tmpBusLine不太相同。

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


输出消息:


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


我已经阅读了几个创建与表结构相同的临时表的示例。输出存储过程,效果很好,但是最好不要提供任何列。

评论

使用SELECT * INTO [TABLE NAME],您确实知道这些列,因为它们是从原始表中复制的。如果我要对存储过程执行相同的操作,这正是我想要的。

参见sommarskog.se/share_data.html和我的文章stackoverflow.com/questions/6215672/…

只是想指出“ select * into tmpBusLine”会创建一个永久表。您可能希望“选择*进入#tmpBusLine”。我敢肯定原始发帖人已经发现了这一点,但它可能会帮助其他人找到该帖子,因为它是当前搜索“ select into temp table”的最佳结果。

我不知道是否已解决此问题,但出现错误的原因是因为from关键字。

Microsoft需要添加SELECT * INTO FROM EXEC!拜托!

#1 楼

您可以为此使用OPENROWSET。看一看。我还包括了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


评论


这是正确的方法。 OPENROWSET几乎是将存储过程的结果视为表表达式的唯一方法。

–罗布·法利
09年8月5日在13:24

仅插入表中似乎有点麻烦。很多配置要做。另外,当我尝试它时,我得到了“消息7357,级别16,状态2,行1无法处理对象” EXEC GetPartyAnalysisData 146”。链接服务器“(null)”的OLE DB访问接口“ SQLNCLI”指示该对象具有没有列,或者当前用户对该对象没有权限。”所以你需要设置一个链接服务器...

–费丁
09年8月10日在12:18

您不需要链接服务器,但是您需要正确获取连接字符串...,并且,指定存储过程的完整路径,包括数据库名称和sp的所有者。

–MartW
09年8月11日在12:30

哎呀!对同一服务器的引用?讨厌。绝对不是必须手动创建临时表的hack

–蒂姆·阿贝尔
2010年11月17日16:16

我同意这是骇客行为,除非您的后背靠墙,否则应该避免。将sp更改为函数可能是一个更好的角度。恕我直言。

– greg
13年4月16日在20:20

#2 楼

如果要在不先声明临时表的情况下执行此操作,则可以尝试创建用户定义的函数而不是存储过程,并使该用户定义的函数返回表。另外,如果要使用存储过程,请尝试以下操作:
CREATE TABLE #tmpBus
(
   COL1 INT,
   COL2 INT
)

INSERT INTO #tmpBus
Exec SpGetRecords 'Params'


评论


我认为关键是生成架构而不必显式声明它。

– Craig
2012年4月26日在16:18

我想知道这与@Aaron Alton的上述解决方案之间的区别是什么。这似乎简单得多,但是我不确定其他任何含义。

–funkymushroom
2012年6月1日17:57

这将起作用,但是如果您向SpGetRecords存储过程中添加了其他列,则该过程将不成功。

–布雷迪·霍尔特(Brady Holt)
2014年1月23日在16:15

每个调用堆栈只能获得一个INSERT INTO EXEC。 SpGetRecords和它调用的任何其他proc都不能在其自己的代码中使用此策略。这会使SpGetRecords的维护人员感到惊讶。

–马特·斯蒂芬森(Matt Stephenson)
2014年4月9日下午5:41

这根本无法回答问题,我也看不出为什么如此反对? OP明确声明“未定义[temp table]”,并且第一行具有create temp table语句。

– NickG
15年4月24日在9:30

#3 楼

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

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


评论


这要求对authors_sales进行预先定义。我正在努力避免这种情况。谢谢。

–费丁
09年3月17日在10:53

我也这么想如此有用,可以快速地插入到tmp表中,但是如果您需要了解从存储的proc返回的数据集结构,则没有那么有用。感谢您的帮助。

–费丁
09年3月17日在10:57

msdn.microsoft.com/zh-cn/library/aa175921.aspx上有一篇不错的文章

–富有的安德鲁斯
09年3月17日在11:07

要使用相同的架构,您可以进行如下复制:从realTable(stackoverflow.com/a/9206463/73794)中选择top 0 *到tempTable中

–连面
16年4月11日在17:34

@EvenMien当我看到您的评论时,我立刻感到很兴奋……但可悲的是,这仅在您proc的结果实际上反映了真实表的情况下才有效:(

– BVernon
4月22日22:33

#4 楼

这是对您问题的稍作修改的答案。如果可以放弃对用户定义的函数使用存储过程,则可以使用内联表值用户定义的函数。本质上,这是一个存储过程(将带有参数),该存储过程返回一个表作为结果集。因此可以很好地放置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函数打算用于的目的(摘自Online Books):


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


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

评论


+1表值函数是合适的解决方案。我们应该注意一些次要缺点:表值函数是一个额外的数据库对象,可能有必要授予它特权。

–spencer7593
2010年8月11日在18:29

喜欢解决方案。我遇到的一个小麻烦是,我的表无法按存储过程中的顺序进行排序。哦,我会整理一下

–mrwaim
2011-3-4在16:49

另一个障碍-“无法从函数内部访问临时表”

–mrwaim
2011-3-5在21:32

最初的问题是我们如何创建一个带sp结果的临时表。这是一个很好的模式,但是没有解决这个问题

– greg
13年4月16日在20:16

greg,我回答的第一行指出:“这是对您的问题的稍作修改的答案。”您的评论是多余的。

–克里斯蒂安·洛里斯(Christian Loris)
13年5月28日在12:51

#5 楼

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

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


评论


获取“消息208,级别16,状态1,行1”无效的对象名称't​​mpBusLine'(可能是由于未预先定义)。

–费丁
09年3月17日在10:54

@Ferds:对不起,起初不理解您的要求。更新了另一个解决方案。

– Quassnoi
09年8月4日在15:35

很好的解决方案。一个警告,您需要在服务器上启用“ DATA ACCESS”:EXEC sp_serveroption'TheServerName','DATA ACCESS',TRUE

– jcollum
09年12月23日在17:39

您还需要允许对服务器的远程访问。这将带来安全后果。

–《 BraveNewMath》
13年5月6日在18:35

如果目标存储过程使用临时表,则将无法使用

–萨尔
17年1月23日在16:13

#6 楼


最简单的解决方案:

CREATE TABLE #temp (...);

INSERT INTO #temp
EXEC [sproc];



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

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


评论


如果我不知道返回结果集的列,那么???我的意思是列可能会有所不同。那么如何将结果插入到临时表中呢???

–SHEKHAR SHETE
16年7月6日在11:47

您可以使用OPENQUERY,但不建议使用它,因为它带有安全漏洞。

– Tigerjz32
16年7月7日在19:26

“如果我不知道返回结果集的列,那么”那么您就不能在逻辑中使用它。如果您不知道数据是什么,将如何使用它们?

– Adriaan Davel
16年7月20日在9:12

@AdriaanDavel我同意您的看法,您应该始终了解您的数据(最佳实践),但是他可能会说,存储过程有时会返回动态列,而您并不总是知道该模式将是什么样。在这种情况下,您可以使用OPENROWSET快速插入和创建表。但是,这样做有明显的安全风险。

– Tigerjz32
16年8月17日在14:22

@nurettin有时您不知道存储过程将要返回什么。在这种情况下会发生什么?您如何创建临时表(当您不知道存储过程将返回什么内容时)并从存储过程中插入到临时表中?

– Tigerjz32
17-10-31在15:54



#7 楼

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

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

评论


+1,很好的建议。您甚至可以将一个快速可选变量添加到名为@TableCreate的sproc或类似的操作(当不为null时,执行上述步骤)。设置完成后,无需更改存储过程。

–伊恩·洛克(Ian Roke)
09年8月27日在15:24

@dotjoe您是否在TEMP表中执行了SELECT INTO操作,并从TEMP表中创建了脚本表?临时表显示在tempdb中,但是我无法右键单击并创建脚本。任何帮助表示赞赏。

–DotnetDude
2012年4月24日18:10

@DotNetDude您可以选择...进入new_table以隐式创建一个实际表。

–dotjoe
2012年4月24日19:09



然后从空表模式中获取粗糙的列定义;最后用合法的TABLE_NAME替换'...':声明@s varchar(max)='';选择@ s = @ s +','+ COLUMN_NAME +''+ DATA_TYPE + isnull('('+ case CHARACTER_MAXIMUM_LENGTH当- 1然后从INFORMATION_SCHEMA.COLUMNS处'max'else cast(CHARACTER_MAXIMUM_LENGTH as varchar(10))end +')',''),其中TABLE_NAME ='...';选择@s

–user423430
17年11月15日在22:10



这是最好的解决方案!

– Lucas925
19-10-3在18:13

#8 楼

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;


评论


没有解决OP原始问题,请先执行插入操作,而不先定义临时表。

–t.durden
17年5月19日在15:31

这正是我想要的。当我无法控制过程时,这可以使我向结果集添加分页

–TheRealChx101
11月9日9:36

#9 楼

如果存储的proc的结果表太复杂而无法手动键入“ create table”语句,并且您不能使用OPENQUERY或OPENROWSET,则可以使用sp_help为您生成列和数据类型的列表。获得列列表后,只需对其进行格式化即可满足您的需求。

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

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

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

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

步骤3:将数据列和类型复制到创建表语句中

我有一个Excel工作表,可用于将sp_help的输出格式化为“创建表”声明。您不需要任何花哨的东西,只需复制并粘贴到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 楼

您的存储过程是否仅检索数据或也对其进行修改?如果仅用于检索,则可以将存储过程转换为函数并使用公用表表达式(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)


评论


这些不是临时表,而是CTE。 technet.microsoft.com/zh-CN/library/…

–yucer
2014年12月3日,13:48

谢谢@yucer ...我相信我当时不知道他们被称为CTE :)

– SO用户
2014年12月4日22:24

#11 楼

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

评论


您忘记了通过@SQL创建表。

– Trisped
15年7月6日在19:38

#12 楼

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

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

评论


如果您不知道服务器名称,请使用SELECT @@ SERVERNAME。您还可以使用EXEC sp_serveroption @@ SERVERNAME,“ DATA ACCESS”,TRUE

– Contango
17年7月1日在13:43

#13 楼

如果您有幸拥有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


评论


优秀的!请注意:如果要对内置存储过程执行此操作,请使用sys.all_objects而不是sys.procedures。

–盖特·阿诺德
18年5月11日在11:35

如果SP在其中使用临时表,这也将失败。 (但是在您的武器库中将其作为proc很方便)

–游览
18年6月20日在3:15

#14 楼

这个存储的proc可以完成这项工作:

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表,然后将其删除。

#15 楼

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


仅存储过程的第一行集可以插入到临时表中表
存储过程必须不执行动态T-SQL语句(sp_executesql
您需要首先定义临时表的结构

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

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


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并同时为多个过程构建临时表定义。

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语句或在存储过程中使用临时表时,上述对象无法定义第一个结果集数据。

评论


关于局限性的实际观察:如果您必须将某些sp(称为SP_LEVEL_0)的输出插入到在另一sp中使用上述方法动态创建的临时表(称为SP_LEVEL_1)中,则无法对此SP_LEVEL_1的输出执行相同的技巧SP_LEVEL_2中的另一个临时表

– nahab
16-09-22在14:12



#16 楼



我正在使用以下架构和数据创建表。


创建存储过程。


现在我知道我的程序的结果是什么,因此我正在执行以下查询。
 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;




#17 楼

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

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

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


#18 楼

代码

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


我希望这会有所帮助。请视情况而定。

#19 楼

我发现将数组/数据表传递到存储过程中可能会给您另一个解决问题的方法。

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

也许可以将其与临时表一起使用。

评论


引入表值参数后,在Sql2008及更高版本中不再需要此功能。现在,您可以直接将.net数据集或datatable对象传递给sql存储过程,而不必执行上述链接中提到的转换为字节

–EndlessSpace
2011年6月10日19:24

#20 楼

我遇到了同样的问题,这是我从保罗的建议中所做的。这里的主要部分是使用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 + ']')


#21 楼

另一种方法是创建一个类型,然后使用PIPELINED将其传递回对象。但是,这仅限于了解各列。但是它具有以下优点:

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


评论


这是什么?似乎与这个问题有关的SQL Server无关

–马丁·史密斯
1月10日18:37

#22 楼

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

执行相同操作的代码:

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


#23 楼

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

SQL Server具有一个内置程序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 楼

如果存储过程仅返回一个表,则可以在SQL Server 2014+中完成此操作。如果有人找到对多个表执行此操作的方法,我想知道这一点。使用它为您构建临时表。然后,您可以按照前面所述从存储过程中填充它。

它的其他变体也适用于Dynamic SQL。

#25 楼

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

#26 楼

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

这有点麻烦,但它是借来的可以从这里的贡献者那里获得,也可以从DBA Stack Exchange的Paul White的解决方案中获得存储过程结果列类型。再次重申,此方法和示例并非针对多用户环境中的流程而设计。在这种情况下,表定义将在全局临时表中设置一小段时间,以供代码生成模板过程参考。

我还没有对它进行充分的测试,所以可能会有一些警告,因此您可能想转到Paul White的答案中的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)作为全局变量,用下划线替换了连字符<再次,我只用简单的存储过程查询和简单的查询对它进行了测试,因此您的工作量可能会有所不同。希望这对某人有帮助。

#27 楼

这是我的带有参数的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);


#28 楼

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

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

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




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




#30 楼

我将执行以下操作


创建(将SP转换为)UDF(表值UDF)。
select * into #tmpBusLine from dbo.UDF_getBusinessLineHistory '16 Mar 2009'


评论


迈出第一步可能会有一些障碍。例如,如果原始SP使用临时表。 UDF不能使用临时表。

–yucer
2014年12月7日在1:48