关于SQL:将存储过程的结果插入临时表

Insert results of a stored procedure into a temporary table

我该怎么做呢?不是FROM [Table],没有定义[temp table]

Select所有从BusinessLinetmpBusLine的数据都可以正常工作。

1
2
3
SELECT *
INTO tmpBusLine
FROM BusinessLine

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

1
2
3
4
SELECT *
INTO tmpBusLine
FROM
EXEC getBusinessLineHistory '16 Mar 2009'

输出消息:

Msg 156, Level 15, State 1, Line 2
Incorrect syntax near the keyword
'exec'.

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


您可以为此使用OpenRowset。看一看。我还包含了sp_configure代码,以启用临时分布式查询(如果尚未启用)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
2
3
4
5
6
7
8
CREATE TABLE #tmpBus
(
   COL1 INT,
   COL2 INT
)

INSERT INTO #tmpBus
EXEC SpGetRecords 'Params'


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

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


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

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

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

1
2
3
4
5
6
7
8
9
10
11
CREATE FUNCTION CustomersByRegion
(  
    @RegionID INT  
)
RETURNS TABLE
AS
RETURN
  SELECT *
  FROM customers
  WHERE RegionID = @RegionID
GO

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

1
SELECT * FROM CustomersbyRegion(1)

或选择进入:

1
SELECT * INTO CustList FROM CustomersbyRegion(1)

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

1
2
3
4
5
6
7
8
9
CREATE PROCEDURE uspCustomersByRegion
(  
    @regionID INT  
)
AS
BEGIN
     SELECT * FROM CustomersbyRegion(@regionID);
END
GO

我认为这是获得预期结果的最"无黑客"方法。它使用现有的功能,因为它们的目的是使用没有额外的并发症。通过在存储过程中嵌套内联表值用户定义函数,可以通过两种方式访问该功能。加上!对于实际的SQL代码,您只有一个维护点。

已经建议使用openrowset,但这不是openrowset函数的用途(从联机丛书中):

Includes all connection information
that is required to access remote data
from an OLE DB data source. This
method is an alternative to accessing
tables in a linked server and is a
one-time, ad hoc method of connecting
and accessing remote data by using OLE
DB. For more frequent references to
OLE DB data sources, use linked
servers instead.

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


1
2
3
4
5
EXEC sp_serveroption 'YOURSERVERNAME', 'DATA ACCESS', TRUE

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


Easiest Solution:

1
2
3
4
CREATE TABLE #temp (...);

INSERT INTO #temp
EXEC [sproc];

如果您不知道模式,那么可以执行以下操作。拜托请注意,此方法存在严重的安全风险。

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


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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
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;


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

1
2
3
4
WITH temp AS (
    SELECT * FROM dbo.fnFunctionName(10, 20)
)
SELECT col1, col2 FROM temp

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

例如,

1
2
3
4
5
6
7
8
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)


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

步骤1:将"into temp"添加到输出查询中(例如,"select[…]into temp from[…]")。

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

步骤2:在临时表上运行sp_帮助。(例如"exec tempdb..sp_help_temp")

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

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

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

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

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

1
2
3
4
5
6
7
8
9
DECLARE @t TABLE
(
   --these columns were copied from sp_help
   COL1 INT,
   COL2 INT  
)

INSERT INTO @t
EXEC spMyProc

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


奎斯诺把我带到了那里,但有一件事不见了:

****我需要在存储过程中使用参数。***并且OpenQuery不允许发生这种情况:

因此,我找到了一种操作系统的方法,也不必使表定义如此严格,并在另一个存储过程中重新定义它(当然,也要抓住可能会中断的机会)!

是的,可以通过以下方式动态创建从存储过程返回的表定义:将openquery语句与伪varaiables一起使用(只要no result set返回字段的数量和位置与具有良好数据的数据集相同)。

一旦创建了表,就可以整天在临时表中使用exec存储过程。

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

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

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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

  • 在临时表中执行存储过程

更新:这对临时表不起作用,所以我不得不手动创建临时表。

Bummer注意:这不适用于临时表,http://www.sommarskog.se/share_data.html openquery

参考:下一件事是定义本地服务器。在示例中,它可能看起来像一个关键字,但实际上它只是一个名称。你就是这样做的:

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

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

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

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

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

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

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


如果openrowset导致了您的问题,那么从2012年起还有另一种方法:使用sys.dm_exec_describe_first_result_set_for_object,如这里所述:检索存储过程的列名和类型?

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 +')'

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

1
2
3
4
5
6
7
8
9
10
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语句执行。


此存储过程执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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表,然后删除它。


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

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

这将创建与过程名称相同的全局临时表。稍后可以根据需要使用临时表。只是别忘了在重新执行之前放下它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    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


  • 我正在创建一个具有以下模式和数据的表。
  • 创建存储过程。
  • 现在我知道了我的过程的结果是什么,所以我正在执行下面的查询。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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

    值(10、5、1、空)将标识"插入"[dbo]。[tbltestingtree]设置为"打开"

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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;

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

  • 只能将存储过程的第一行集插入临时表中
  • 存储过程不能执行动态T-SQL语句(sp_executesql)
  • 您需要首先定义临时表的结构
  • 以上可能看起来有局限性,但我认为完全有道理——如果使用sp_executesql,您可以返回两列一次,返回十列一次,如果有多个结果集,也不能将它们插入多个表中——您可以在一条T-SQL语句中最多插入两个表(使用OUTPUT子句,不使用触发器)。

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

    • sys.dm_exec_describe_first_result_set_for_对象
    • sys.dm_exec_describe_first_result_set
    • 描述第一个结果集

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

    1
    2
    3
    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字段,因为它可能非常有用。它存储列的完整定义。例如:

    1
    2
    3
    4
    5
    6
    7
    smalldatetime
    nvarchar(MAX)
    uniqueidentifier
    nvarchar(1000)
    REAL
    smalldatetime
    DECIMAL(18,2)

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

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

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


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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    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

    我发现将数组/数据表传递到存储过程中,这可能会让您对如何解决问题有另一个了解。

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

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


    代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    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

    我希望这有帮助。请酌情确认。


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

    1
    2
    3
    4
    5
    6
    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 + ']')

    另一种方法是创建一个类型,并使用流水线来传递对象。但是,这仅限于了解列。但它的优势在于能够做到:

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

    如果您知道正在传递的参数,并且您没有权限进行sp_配置,那么可以使用这些参数编辑存储过程,这些参数可以存储在全局表中。


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

    执行相同操作的代码:

    1
    2
    3
    4
    5
    CREATE TABLE #tempTable (Column1 INT, Column2 VARCHAR(MAX));
    INSERT INTO #tempTable
    EXEC [app].[Sproc_name]
    @param1 = 1,
    @param2 =2;

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    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无法确定存储过程的结果(例如,如果它使用临时表),则此操作将不起作用。


    这可以在SQL Server 2014+中完成,前提是sp只返回一个表。如果有人能为多张桌子找到这样的方法,我很想知道。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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填充它。

    还有一些变体也可以与动态SQL一起使用。


    我会做以下的

  • 创建(将sp转换为)UDF(表值UDF)。

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