Insert results of a stored procedure into a temporary table
Asked Answered
I

35

1841

How do I do a SELECT * INTO [temp table] FROM [stored procedure]? Not FROM [Table] and without defining [temp table]?

Select all data from BusinessLine into tmpBusLine works fine.

select *
into tmpBusLine
from BusinessLine

I am trying the same, but using a stored procedure that returns data, is not quite the same.

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

Output message:

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

I have read several examples of creating a temporary table with the same structure as the output stored procedure, which works fine, but it would be nice to not supply any columns.

Ingenerate answered 17/3, 2009 at 10:45 Comment(10)
With SELECT * INTO [TABLE NAME] you do know the columns, as they are copied from the original table. This is exactly what I want if I were to do the same thing against a stored procedure.Ingenerate
See sommarskog.se/share_data.html and my post #6216172Cormophyte
Just want to point out that "select * into tmpBusLine" creates a permanent table. You probably want "select * into #tmpBusLine". I'm sure the original poster has already found this out but it might help others that find this post as it is the top result currently for the search "select into temp table"Deportation
I don't know if this has been addressed or not but the reason why you get the error is because of the from keyword.Neckar
You should use OPENROWSET.Milliken
OPENROWSET not supported in SQL Azure (along with a bunch of other missing features). What a crippled version. social.technet.microsoft.com/Forums/WINDOWS/en-US/… I see that I commented on this issue 7 years ago, and it's ridiculous there's still no easy way to select the results of a stored procedure into a new table. You have to manually go in an inspect the returned columns and create table definition by hand, then use 'insert' syntax rather than 'select into'. Now openrowset is a no-go.Cormophyte
DECLARE @temptable TABLE{ column 1 <datatype>... column n <datatype> }Beef
Microsoft needs to add SELECT * INTO FROM EXEC! Please!Pict
"Microsoft" is not listening here :) They even retired MS Connect so if you had asked on a channel where they were listening, they could could stop listeningKiley
I'm back. Commenting again, not 7 years later like my last comment, but a full 10 years after my original comment in 2011. Just adding that we can't do this in AWS RDS either, as OPENROWSET is restricted and produces the following error: "SQL Server blocked access to STATEMENT 'OpenRowset/OpenDatasource' of component 'Ad Hoc Distributed Queries' because this component is turned off as part of the security configuration for this server. A system administrator can enable the use of 'Ad Hoc Distributed Queries' by using sp_configure."Cormophyte
L
769

You can use OPENROWSET for this. Have a look. I've also included the sp_configure code to enable Ad Hoc Distributed Queries, in case it isn't already enabled.

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
Laminar answered 4/8, 2009 at 15:27 Comment(18)
This is the right way to do it. OPENROWSET is pretty much the only way to treat the results of a stored procedure as a table expression.Worden
This seems a bit cumbersome just to insert into a table. A lot of configuring to do. Also when I tried it I got "Msg 7357, Level 16, State 2, Line 1 Cannot process the object "EXEC GetPartyAnalysisData 146". The OLE DB provider "SQLNCLI" for linked server "(null)" indicates that either the object has no columns or the current user does not have permissions on that object." So you need to set a linked server...Ingenerate
You don't need a linked server, but you would need to get the connection string right...and also, specify the full path to the stored procedure including the database name and the sp's owner.Picayune
Indeed, that error you're getting when you try this is what I get when I try to access an object that isn't in the same database as the default db for the OPENROWSET connection (which is probably 'master').Picayune
On 2nd thought, this solution worked better for me because setting server level settings with sp_serveroption is verboten around here.Telpherage
eeeeew! a reference to the same server? nasty. definitely more of a hack than having to manually create the temp tableStricken
-it is cool, but it does not work if the stored procedure declare a temp table inside, I've tried with lots of stored procedure and everything is ok, but if I use a #tablename inside the stored I got a invalid object #tablename.Bertelli
What happens here if the SP returns more than one resultset?Twoseater
@Bertelli put SET FMTONLY OFF; in your OPENQUERY call before the actual SP execution, that will make it work with temp tables. Just be aware that the SP will be run twice.Wilie
i agree that this is a hack and should probably be avoided unless your back is against the wall. Changing the sp to a function is probably a better angle to take. IMHO.Apogee
Watch out for Duplicate column names are not allowed in result sets obtained through OPENQUERY and OPENROWSET. The column name "..." is a duplicate.Deaton
What if my SP need parameters as well? E.g., Exec SpGetRecords 'Params1', 'Params2', how should I properly quote them?Wershba
@Apogee One problem with changing a SP to a function is that functions have ore restrictions that SPs. There are stuff you can do in a SP that you can't in a function (e.g. call another SP, edit tables)Outwit
Bear in mind that the EXEC string has to be a literal string - you can't pass in a calculated string, which means you can't really pass parameters.Kata
OPENROWSET not supported in SQL Azure?Cormophyte
Useless in SQL Azure. Not supported.Cormophyte
this doesn't work if a stored proc contains dynamic sql or uses temp tables unfortunatelyLeija
Update the link to OPENROWSET docs. New link https://learn.microsoft.com/en-us/sql/t-sql/functions/openrowset-transact-sql?view=sql-server-ver15.Untouchability
S
742

If you want to do it without first declaring the temporary table, you could try creating a user-defined function rather than a stored procedure and make that user-defined function return a table. Alternatively, if you want to use the stored procedure, try something like this:

CREATE TABLE #tmpBus
(
   COL1 INT,
   COL2 INT
)

INSERT INTO #tmpBus
Exec SpGetRecords 'Params'
Scottyscotus answered 17/3, 2009 at 14:8 Comment(13)
I think the point was to generate the schema without having to explicitly declare it.Rosettarosette
I would be interested to know what the difference between this and @Aaron Alton's solution above. This one seems far simpler, but I am unsure as to any other implications.Larrabee
@Larrabee - Yes there are implications to this. UDF's can't do certain things SPROCS can do like Common table expressions.Gushy
This will work but if you ever add additional columns to the SpGetRecords stored procedure, this will blow up.Smokestack
You only get one INSERT INTO EXEC per call stack. SpGetRecords and any other proc it calls may not use this strategy in their own code. This can surprise maintainers of SpGetRecords.Jolson
@MattStephenson Is there a workaround without OPENROWSET for that situation (multiple/"hidden" INSERT INTO EXECs) that you've found?Monophysite
@Monophysite I don't know another workaround. I've often picked the least complex SP and turned (part of) it into a TVF.Jolson
Yeah, that's essentially what I ended up doing in a similar situation: sp_helptext the sproc's code and hack in what changes you need in a new, custom version. Thanks.Monophysite
This doesn't answer the question at all and I don't see why it's so upvoted? The OP explicitly stated "without defining [temp table]" and your very first line has a create temp table statement.Wilkison
This works fine. No permissions through sp_configure needed.Astred
This can be misleading. It works on some scenarios, but it's not answering OP's question.Hallee
"Not FROM [Table] and without defining [temp table]?"Organza
I get "An INSERT EXEC statement cannot be nested." error.Lemal
B
319

In SQL Server 2005 you can use INSERT INTO ... EXEC to insert the result of a stored procedure into a table. From MSDN's INSERT documentation (for SQL Server 2000, in fact):

--INSERT...EXECUTE procedure example
INSERT author_sales EXECUTE get_author_sales
Bacteriostasis answered 17/3, 2009 at 10:50 Comment(7)
This requires the authors_sales to be defined up front. I am trying to avoid this. Thanks.Ingenerate
I thought as much. So useful Inserting into tmp tables on the fly, but not so useful if you need to know the dataset structure returned from a stored proc. Thanks for you help.Ingenerate
There's a good article here msdn.microsoft.com/en-us/library/aa175921.aspxCharmainecharmane
To use the same schema, you can make a copy as follows: select top 0 * into tempTable from realTable (https://mcmap.net/q/45862/-best-way-to-create-a-temp-table-with-same-columns-and-type-as-a-permanent-table)Whap
@EvenMien I momentarily got excited when I saw your comment... but sadly that only works if the results of your proc actually mirror a real table :(Higginbotham
"Not FROM [Table] and without defining [temp table]?"Organza
@EvenMien... it doesnt have to mimic a real table. It can reference a table type or even a table variable defined in the calling SP. This is not the perfect solution whewre you could capture a random sp , but it is helpful when you have spA whose results are needed in multiple places. IMHO, it beats all the currently available options.Apogee
V
211

This is an answer to a slightly modified version of your question. If you can abandon the use of a stored procedure for a user-defined function, you can use an inline table-valued user-defined function. This is essentially a stored procedure (will take parameters) that returns a table as a result set; and therefore will place nicely with an INTO statement.

Here's a good quick article on it and other user-defined functions. If you still have a driving need for a stored procedure, you can wrap the inline table-valued user-defined function with a stored procedure. The stored procedure just passes parameters when it calls select * from the inline table-valued user-defined function.

So for instance, you'd have an inline table-valued user-defined function to get a list of customers for a particular region:

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

You can then call this function to get what your results a such:

SELECT * FROM CustomersbyRegion(1)

Or to do a SELECT INTO:

SELECT * INTO CustList FROM CustomersbyRegion(1)

If you still need a stored procedure, then wrap the function as such:

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

I think this is the most 'hack-less' method to obtain the desired results. It uses the existing features as they were intended to be used without additional complications. By nesting the inline table-valued user-defined function in the stored procedure, you have access to the functionality in two ways. Plus! You have only one point of maintenance for the actual SQL code.

The use of OPENROWSET has been suggested, but this is not what the OPENROWSET function was intended to be used for (From Books Online):

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.

Using OPENROWSET will get the job done, but it will incur some additional overhead for opening up local connections and marshalling data. It also may not be an option in all cases since it requires an ad hoc query permission which poses a security risk and therefore may not be desired. Also, the OPENROWSET approach will preclude the use of stored procedures returning more than one result set. Wrapping multiple inline table-value user-defined functions in a single stored procedure can achieve this.

Vanden answered 4/8, 2009 at 17:11 Comment(5)
+1 A table-valued function is an appropriate solution. We should make note of the minor drawbacks: the table-valued function is an extra database object, and it may be necessary to grant privileges on it.Devaughn
Love the solution. One minor snag I hit, is that my table cant have order by where as it could have it in the stored procedure. Owh well, i'll sort it outNigrify
One more snag - "Cannot access temporary tables from within a function"Nigrify
The original question is how do we create a temp table w/ the results of the sp. This is a good pattern, but doesnt address this questionApogee
greg, the first line in my answer states "This is an answer to a slightly modified version of your question." Your comment is redundant.Vanden
C
150
Select @@ServerName
EXEC sp_serveroption @@ServerName, 'DATA ACCESS', TRUE

SELECT  *
INTO    #tmpTable
FROM    OPENQUERY(YOURSERVERNAME, 'EXEC db.schema.sproc 1')
Councilman answered 17/3, 2009 at 10:50 Comment(6)
Get a "Msg 208, Level 16, State 1, Line 1 Invalid object name 'tmpBusLine' (probably as it's not define up front).Ingenerate
@Ferds: sorry, didn't understand your request at first. Updated with another solution.Councilman
Great solution. One caveat, you'll need to enable 'DATA ACCESS' on your server: EXEC sp_serveroption 'TheServerName', 'DATA ACCESS', TRUETelpherage
You'll also need to allow remote access to the server. This will have security ramifications.Dygert
Perfect. This works with existing linked servers and allows me to exec remote table valued functions, which is not supported directly.Raynor
This will not work if the target stored procedure uses temp tablesMunicipalize
R
133

Easiest Solution:

CREATE TABLE #temp (...);

INSERT INTO #temp
EXEC [sproc];

If you don't know the schema then you can do the following. Please note that there are severe security risks in this method.

SELECT * 
INTO #temp
FROM OPENROWSET('SQLNCLI', 
                'Server=localhost;Trusted_Connection=yes;', 
                'EXEC [db].[schema].[sproc]')
Residency answered 13/3, 2015 at 19:38 Comment(14)
if i dont know the column of returned resultset then??? i mean column may vary. so how to insert the result into temp table???Ely
You could use OPENQUERY but it is not recommended as it comes with security flaws.Residency
"if i dont know the column of returned resultset then" then you cant use it in your logic. How will you use the data if you don't know what it is?Stalinabad
@AdriaanDavel I agree with you that you should always know your data (best practice), however what he might be saying is that there are times when the sproc returns dynamic columns and you don't always know what the schema will look like. In that case, you can use OPENROWSET to insert and create a table on the fly. However, there are obvious security risks with doing this...Residency
SQLNCLI is invalid for me? how do you know that is the OLEDB provider?Myatt
There is no logic in saying "how will you use your data if you don't know the schema". Schema can be inferred from sys.columns. Even for temporary tables.Shawndashawnee
@Shawndashawnee sometimes you don't know what the stored procedure is going to return. What happens in that case? How could you create a temp table (when you don't know what the stored procedure will return) and insert into it from a stored procedure?Residency
@Residency using openrowset, obviously.Shawndashawnee
@Shawndashawnee That makes no sense. You still need to know the schema don't you? What if you don't know the schema, what do you do?Residency
@Residency as I said, SELECT * FROM tempdb.sys.columns WHERE object_id = OBJECT_ID('tempdb..#tmp');Shawndashawnee
@Shawndashawnee what do you do with that information? The question is, how do you insert a sproc's result into a table, given that you don't know what the sproc will return. How would you code that?Residency
@Residency I'm not saying I would insert it into a table. Using might just mean showing it on a screen or summing the rows or completing missing data. It isn't always taking data from one table and putting it into another like you're used to.Shawndashawnee
I suppose if you had to insert, you would match the column names and types to create an insert using the select I showed you with the target table.Shawndashawnee
@Residency I know what the question said, but I can't just let someone on the internet be wrong :-)Shawndashawnee
D
132

When the stored procedure returns a lot of columns and you do not want to manually "create" a temporary table to hold the result, I've found the easiest way is to go into the stored procedure and add an "into" clause on the last select statement and add 1=0 to the where clause.

Run the stored procedure once and go back and remove the SQL code you just added. Now, you'll have an empty table matching the stored procedure's result. You could either "script table as create" for a temporary table or simply insert directly into that table.

Dumpish answered 17/3, 2009 at 14:6 Comment(6)
+1, excellent suggestion. You could even add a quick optional variable to the sproc called @TableCreate or something similar that when is not null do the steps above. Doesn't require changing of the sproc then once it is set up.Gerfalcon
@Dumpish Do you do a SELECT INTO a temp table and do a script table as create from the temp table? Temp tables show up in tempdb but I am unable to do a right click and do a create script. Any help is appreciated.Averir
@DotNetDude you can select ... into new_table to implicitly create an actual table.Dumpish
Then grab rough column definition from empty table schema; replace '...' at the end with legit TABLE_NAME: declare @s varchar(max)='';select @s=@s+','+COLUMN_NAME+' '+DATA_TYPE+isnull('('+case CHARACTER_MAXIMUM_LENGTH when -1 then 'max' else cast(CHARACTER_MAXIMUM_LENGTH as varchar(10))end+')','')from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='...';select @sSherbet
This is the best solution!Guss
Best solution by far. I'll put this in my bag of tricks, lol.Quartis
G
68
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;
Grub answered 24/3, 2011 at 7:15 Comment(2)
doesn't address OP original question, doing the insert with out defining the temp table first.Stephanistephania
This is exactly what I was looking for. This allows me to add paging to the result set when I have no control of the procedureEnough
A
56

If the results table of your stored proc is too complicated to type out the "create table" statement by hand, and you can't use OPENQUERY OR OPENROWSET, you can use sp_help to generate the list of columns and data types for you. Once you have the list of columns, it's just a matter of formatting it to suit your needs.

Step 1: Add "into #temp" to the output query (e.g. "select [...] into #temp from [...]").

The easiest way is to edit the output query in the proc directly. if you can't change the stored proc, you can copy the contents into a new query window and modify the query there.

Step 2: Run sp_help on the temp table. (e.g. "exec tempdb..sp_help #temp")

After creating the temp table, run sp_help on the temp table to get a list of the columns and data types including the size of varchar fields.

Step 3: Copy the data columns & types into a create table statement

I have an Excel sheet that I use to format the output of sp_help into a "create table" statement. You don't need anything that fancy, just copy and paste into your SQL editor. Use the column names, sizes, and types to construct a "Create table #x [...]" or "declare @x table [...]" statement which you can use to INSERT the results of the stored procedure.

Step 4: Insert into the newly created table

Now you'll have a query that's like the other solutions described in this thread.

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

INSERT INTO @t 
Exec spMyProc 

This technique can also be used to convert a temp table (#temp) to a table variable (@temp). While this may be more steps than just writing the create table statement yourself, it prevents manual error such as typos and data type mismatches in large processes. Debugging a typo can take more time than writing the query in the first place.

Andraandrade answered 31/7, 2012 at 17:9 Comment(0)
P
49

Does your stored procedure only retrieve the data or modify it too? If it's used only for retrieving, you can convert the stored procedure into a function and use the Common Table Expressions (CTEs) without having to declare it, as follows:

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

However, whatever needs to be retrieved from the CTE should be used in one statement only. You cannot do a with temp as ... and try to use it after a couple of lines of SQL. You can have multiple CTEs in one statement for more complex queries.

For example,

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)
Pointblank answered 6/8, 2009 at 7:28 Comment(2)
These are not temporary tables, are CTEs. technet.microsoft.com/en-us/library/…Calc
Thanks @Calc ... I believe I didn't know they were called CTEs back then :)Pointblank
F
48

If the OPENROWSET is causing you issues, there is another way from 2012 onwards; make use of sys.dm_exec_describe_first_result_set_for_object, as mentioned here: Retrieve column names and types of a stored procedure?

First, create this stored procedure to generate the SQL for the temporary table:

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

To use the procedure, call it in the following way:

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

Note that I'm using a global temporary table. That's because using EXEC to run the dynamic SQL creates its own session, so an ordinary temporary table would be out of scope to any subsequent code. If a global temporary table is a problem, you can use an ordinary temporary table, but any subsequent SQL would need to be dynamic, that is, also executed by the EXEC statement.

Ferrante answered 5/2, 2014 at 16:21 Comment(2)
You forgot to create the table from @SQL.Spectacular
sys.dm_exec_describe_first_result_set_for_object gives error while we have temp table in usp_YourProcedure: "The metadata could not be determined because statement ... uses a temp table"Compton
T
34

Quassnoi put me most of the way there, but one thing was missing:

****I needed to use parameters in the stored procedure.****

And OPENQUERY does not allow for this to happen:

So I found a way to work the system and also not have to make the table definition so rigid, and redefine it inside another stored procedure (and of course take the chance it may break)!

Yes, you can dynamically create the table definition returned from the stored procedure by using the OPENQUERY statement with bogus varaiables (as long the NO RESULT SET returns the same number of fields and in the same position as a dataset with good data).

Once the table is created, you can use exec stored procedure into the temporary table all day long.


And to note (as indicated above) you must enable data access,

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

Code:

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

Thanks for the information which was provided originally... Yes, finally I do not have to create all these bogus (strict) table defintions when using data from another stored procedure or database, and yes you can use parameters too.

Search reference tags:

  • SQL 2005 stored procedure into temp table

  • openquery with stored procedure and variables 2005

  • openquery with variables

  • execute stored procedure into temp table

Update: this will not work with temporary tables so I had to resort to manually creating the temporary table.

Bummer notice: this will not work with temporary tables, http://www.sommarskog.se/share_data.html#OPENQUERY

Reference: The next thing is to define LOCALSERVER. It may look like a keyword in the example, but it is in fact only a name. This is how you do it:

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

To create a linked server, you must have the permission ALTER ANY SERVER, or be a member of any of the fixed server roles sysadmin or setupadmin.

OPENQUERY opens a new connection to SQL Server. This has some implications:

The procedure that you call with OPENQUERY cannot refer temporary tables created in the current connection.

The new connection has its own default database (defined with sp_addlinkedserver, default is master), so all object specification must include a database name.

If you have an open transaction and are holding locks when you call OPENQUERY, the called procedure can not access what you lock. That is, if you are not careful you will block yourself.

Connecting is not for free, so there is a performance penalty.

Trophoplasm answered 11/2, 2010 at 19:39 Comment(1)
If you don't know your server name, use SELECT @@SERVERNAME. You can also use EXEC sp_serveroption @@SERVERNAME, 'DATA ACCESS', TRUEInobservance
T
30

If you're lucky enough to have SQL 2012 or higher, you can use dm_exec_describe_first_result_set_for_object

I have just edited the sql provided by gotqn. Thanks gotqn.

This creates a global temp table with name same as procedure name. The temp table can later be used as required. Just don't forget to drop it before re-executing.

    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
Thrift answered 26/2, 2016 at 11:5 Comment(3)
Excellent! Just one remark: use sys.all_objects instead of sys.procedures if you want to do this for built-in stored procedures.Cammie
This will also fail if the SP uses temporary tables within it. (but its pretty handy to have this as a proc in your arsenal)Potpourri
This works with Azure SQL whereas OPENROWSET does not.Whap
D
24

This stored proc does the job:

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

It's a slight rework of this: Insert stored procedure results into table so that it actually works.

If you want it to work with a temporary table then you will need to use a ##GLOBAL table and drop it afterwards.

Dincolo answered 26/9, 2011 at 16:4 Comment(0)
I
20

In order to insert the first record set of a stored procedure into a temporary table you need to know the following:

  1. only the first row set of the stored procedure can be inserted into a temporary table
  2. the stored procedure must not execute dynamic T-SQL statement (sp_executesql)
  3. you need to define the structure of the temporary table first

The above may look as limitation, but IMHO it perfectly makes sense - if you are using sp_executesql you can once return two columns and once ten, and if you have multiple result sets, you cannot insert them into several tables as well - you can insert maximum in two table in one T-SQL statement (using OUTPUT clause and no triggers).

So, the issue is mainly how to define the temporary table structure before performing the EXEC ... INTO ... statement.

The first works with OBJECT_ID while the second and the third works with Ad-hoc queries as well. I prefer to use the DMV instead of the sp as you can use CROSS APPLY and build the temporary table definitions for multiple procedures at the same time.

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;

Also, pay attention to the system_type_name field as it can be very useful. It stores the column complete definition. For, example:

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

and you can use it directly in most of the cases to create the table definition.

So, I think in most of the cases (if the stored procedure match certain criteria) you can easily build dynamic statements for solving such issues (create the temporary table, insert the stored procedure result in it, do what you need with the data).


Note, that the objects above fail to define the first result set data in some cases like when dynamic T-SQL statements are executed or temporary tables are used in the stored procedure.

Innate answered 3/6, 2015 at 11:47 Comment(1)
practical observation on limitations: if you have to insert output of some sp (lets call it SP_LEVEL_0) to temp table dynamically created using above approach in another sp (lets call it SP_LEVEL_1), you cannot do the same trick for output of this SP_LEVEL_1 to some another temp table in SP_LEVEL_2Marketplace
V
18
  1. I'm creating a table with the following schema and data.

  2. Create a stored procedure.

  3. Now I know what the result of my procedure is, so I am performing the following query.

     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;
    
Verbosity answered 5/10, 2013 at 4:3 Comment(0)
E
16

If the query doesn't contain parameter, use OpenQuery else use OpenRowset.

Basic thing would be to create schema as per stored procedure and insert into that table. 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
Eskimo answered 7/9, 2015 at 11:37 Comment(0)
W
13

Code

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

I hope this helps. Please qualify as appropriate.

Without answered 10/3, 2011 at 18:30 Comment(0)
R
11

I found Passing Arrays/DataTables into Stored Procedures which might give you another idea on how you might go solving your problem.

The link suggests to use an Image type parameter to pass into the stored procedure. Then in the stored procedure, the image is transformed into a table variable containing the original data.

Maybe there is a way this can be used with a temporary table.

Recompense answered 6/8, 2009 at 7:49 Comment(1)
This is no longer required in versions Sql2008 & later with the introduction of Table Value Parameters. Now you can directly pass a .net dataset or datatable object to a sql stored procedure with having to do conversion to byte as mentioned in the above linkSheliasheline
S
10

I met the same problem and here is what I did for this from Paul's suggestion. 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 + ']')
Scotia answered 18/8, 2014 at 9:11 Comment(0)
A
9

Another method is to create a type and use PIPELINED to then pass back your object. This is limited to knowing the columns however. But it has the advantage of being able to do:

SELECT * 
FROM TABLE(CAST(f$my_functions('8028767') AS my_tab_type))
Ability answered 11/8, 2009 at 12:46 Comment(1)
What is this? Doesnt seem to be anything to do with SQL Server which this question is aboutGrays
E
9

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

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

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

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

EXEC (@strSQL)

INSERT INTO myTableName

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

SELECT * FROM myTableName

DROP TABLE myTableName

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 stored procedure as stated before.

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

Exigent answered 27/1, 2017 at 11:52 Comment(1)
For earlier versions of SQL Server, the stored procedure sp_describe_first_result_set does very nearly the same thing. There is a blog article about the same time as your answer: Using a temporary table to handle sp_describe_first_result_set that creates a table from another resultset. Then insert into the created table. If MS had implemented an EXEC version of SELECT INTO, we wouldn't have this grief. Thanks for jogging my memory!Cathedral
C
8

After searching around I found a way to create a temp table dynamically for any stored procedure without using OPENROWSET or OPENQUERY using a generic schema of Stored Procedure's result definition especially when you are not database Administrator.

Sql server has a buit-in proc sp_describe_first_result_set that can provide you with schema of any procedures resultset. I created a schema table from results of this procedure and manually set all the field to 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 

Developed and tested on Sql Server version - Microsoft SQL Server 2016 (RTM) - 13.0.1601.5(Build 17134:)

You can tweak the schema for your SQL server version that you are using (if needed).

Cache answered 13/6, 2019 at 18:26 Comment(0)
T
7

It's a simple 2 step process: - create a temporary table - Insert into the temporary table.

Code to perform the same:

CREATE TABLE #tempTable (Column1 int, Column2 varchar(max));
INSERT INTO #tempTable 
EXEC [app].[Sproc_name]
@param1 = 1,
@param2 =2;
Tuttle answered 27/4, 2019 at 10:48 Comment(0)
E
4

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.

Evolutionary answered 24/3, 2015 at 9:1 Comment(0)
B
3

A few years late to the question, but I needed something like this for some quick and dirty code generation. I believe as others have stated it is just easier to define the temp table up front, but this method should work for simple stored procedure queries or sql statments.

This will be a little convoluted, but it borrows from the contributors here as well as Paul White's solution from DBA Stack Exchange Get stored procedure result column-types. Again, to reiterate this approach & example is not designed for processes in a multi user environment. In this case the table definition is being set for a short time in a global temp table for reference by a code generation template process.

I haven't fully tested this so there may be caveats so you may want to go to the MSDN link in Paul White's answer. This applies to SQL 2012 and higher.

First use the stored procedure sp_describe_first_result_set which resembles Oracle's describe.

This will evaluate the first row of the first result set so if your stored procedure or statement returns multiple queries it will only describe the first result.

I created a stored proc to break down the tasks that returns a single field to select from to create the temp table definition.

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

The conundrum is that you need to use a global table, but you need to make it unique enough so you can drop and create from it frequently without worrying about a collision.
In the example I used a Guid (FE264BF5_9C32_438F_8462_8A5DC8DEE49E) for the global variable replacing the hyphens with underscore

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

Again, I have only tested it with simple stored procedure queries and simple queries so your mileage may vary. Hope this helps someone.

Begat answered 20/9, 2019 at 21:16 Comment(0)
C
3

Here is my T-SQL with parameters

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

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


--the query
DECLARE @param1 int = 1, @param2 int = 2
DECLARE @SQLStr varchar(max) = 'SELECT * INTO #MyTempTable
                                FROM OPENROWSET(''SQLNCLI'',  
''Server=ServerName;Database=DbName;Trusted_Connection=yes'',
''exec StoredProcedureName '+ CAST(@param1 AS varchar(15)) +','+ CAST(@param2 AS varchar(15)) +''') AS a ;
 select * from #MyTempTable;
 drop table #MyTempTable        
';
EXECUTE(@SQLStr);
Cleromancy answered 15/6, 2020 at 11:49 Comment(0)
D
2

If you let dynamic SQL create a temp table, this table is owned by the Dynamic SQL connection, as opposed to the connection your stored procedure is called from.

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;

Msg 208, Level 16, State 0 Invalid object name '#Pivoted'. This is because #Pivoted is owned by the Dynamic SQL connection. So the last instruction

SELECT * FROM #Pivoted

fails.

One way to not face this issue is to make sure all references to #Pivoted are made from inside the dynamic query itself:

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

Decrescent answered 29/11, 2019 at 16:9 Comment(2)
It's the same 'connection' and a different 'scope' (much as if a nested SP was called which defined the local temp table).Oxbow
Create a global temp table ##Pivoted inside the dynamic query and then it will be accessible from the outside.Trisa
E
1

Well, you do have to create a temp table, but it doesn't have to have the right schema....I've created a stored procedure that modifies an existing temp table so that it has the required columns with the right data type and order (dropping all existing columns, adding new columns):

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

Note this won't work if sys.dm_exec_describe_first_result_set_for_object can't determine the results of the stored procedure (for instance if it uses a temp table).

Extramundane answered 10/7, 2018 at 14:58 Comment(0)
G
1

Old post but useful. In addition to other answers, one can also capture the output of a stored procedure in to a table-variable (as asker wants to avoid the use of #temp-table), I therefore created a fully working example using @table-variable:

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

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

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

HTH.

Gratify answered 30/12, 2022 at 15:12 Comment(4)
Probably only inserts the first result set, right? Or just the last? Or tries to insert from each result set?Beasley
@Paul-SebastianManole, in case if stored proc is returning multiple resultsets, if the columns of all resultsets match (same number of columns, datatypes etc.) then output of all the resultsets will be combined (with column-headers of the last resultset will be used), however, if columns don't match then attempt to store outputs of multiple resultsets in table-variable will generate following error: "Column name or number of supplied values does not match table definition". Unfortunately SQL Server doesn't provide a way to capture only one of the outputs/resultsets. HTH.Gratify
I don't know why, not working here...Lemaster
@Monzur, what's not working, what error are you getting?Gratify
E
0

First, modify your stored procedure to save the end results in to a temp table. By doing this we are creating a table matching with the SP output fields. And then have a select statement to save that temp table to a any table name. Then execute the SP as explained in step 2

Step 1: modify your stored procedure to save the end results in to a temp table

[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

Step 2: Execute the SP as below that will insert records to your table

Insert SP_Output_Table_1
EXE  You_SP_Nane @Parameter1 = 52, @parameter2 =1
Eyde answered 17/6, 2021 at 23:21 Comment(1)
Basically the same as @Dumpish https://mcmap.net/q/45067/-insert-results-of-a-stored-procedure-into-a-temporary-table; however, the two answers together are best for providing detail and clarity.Debose
S
0

Easiest solution: In case your can modify the SP SLIGHTLY.

I wanted to share this because I tried all the above and nothing worked for security reason so I ended up doing this very simple modification.

Whatever your SP returns INSERT those results in a temp table with ## (## makes it global temp, # would make it local only) So at the end of your SP, when the results are produced, transform:

SELECT...;

by:

INSERT INTO ##temp;

SELECT...;

SELECT * FROM ##temp;

Now in whatever script you want to use the results, simply use ##temp as a regular table after executing

EXEC...;

Snob answered 31/3, 2023 at 15:18 Comment(0)
G
0

I had a similar requirement. One proc that processed data (in this case uploaded spreadsheets) and put the data into a table with column headers based on the first row. The proc that called this would not know what the schema was for the table being passed back to it. During my searching for a solution, this thread kept popping up so I thought I would reply.

Instead of trying to pass a table from one proc to another, I used the scope of temp tables.

ALTER PROCEDURE [dbo].[ParentProc]

AS
BEGIN
    IF(OBJECT_ID('tempdb..#LoadTable')) IS NOT NULL DROP TABLE #LoadTable

    CREATE TABLE #LoadTable(DUMMY nVarChar(max))

    EXEC [dbo].[FetchExcelSheet] 'C:\Temp\ExcelVerizonUploadTest.csv'

    -- Process your data here

    IF(OBJECT_ID('tempdb..##LoadTable')) IS NOT NULL DROP TABLE #LoadTable

END

This is the proc that loads the data from the excel sheets.

ALTER PROCEDURE [dbo].[FetchExcelSheet]
    -- Add the parameters for the stored procedure here
        @pURL nVarChar(max) 

AS
BEGIN

    SET NOCOUNT ON;


    -- ************ IMPORTANT ****************************
    -- IF YOU  PUT  A TICKET IN AND HAVE NOT READ THIS. I WILL MOCK YOU!!!!
    -- #LOADTABLE MUST BE CREATED IN THE PARENT PROC.
    --CREATE TABLE #LoadTable(DUMMY nVarChar(max))

    IF(OBJECT_ID('tempdb..#ColumnNames')) IS NOT NULL  DROP TABLE 
    #ColumnNames

    IF(OBJECT_ID('tempdb..#FirstRowHold')) IS NOT NULL DROP TABLE 
     #FirstRowHold
         

    CREATE TABLE #FirstRowHold(DUMMY nVarChar(max))

    -- Move the first row of the csv file into the HoldTable
    DECLARE @pFirstRowSQL nVarChar(max)        
    SET @pFirstRowSQL = '          
    BULK INSERT #FirstRowHold   FROM ''' + @pURL + '''
    WITH (FORMAT = ''CSV'',
                FirstRow = 1,
                LastRow = 1,
                ROWTERMINATOR = ''\n'');'
    EXEC (@pFirstRowSQL)


    -- Split the string and put it into individual rows in the NameHold table.
    SELECT * INTO #ColumnNames FROM STRING_SPLIT((SELECT TOP 1 DUMMY FROM #FirstRowHold) ,',')
    
    -- Add Identity in for serialization and duplication check
    ALTER TABLE #ColumnNames ADD ID INT IDENTITY

    -- Append a serial number to duplicate values.
    UPDATE #ColumnNames     
        SET value = value + CAST(ID AS nVarChar(max)) WHERE VALUE IN (
    SELECT value from #ColumnNames GROUP BY value HAVING COUNT(*) > 1)
    
    
    DECLARE @TempID nVArChar(max)
    
    
    DECLARE @StripID nVarChar(max)
     
     -- Cycle through the ColumnName table and add each as a column
     WHILE(SELECT COUNT(*) FROM #ColumnNames) > 0
     BEGIN

             SELECT TOP 1 @TempID = (SELECT TOP 1 VALUE FROM #ColumnNames) 

             SET @StripID = dbo.[udf-Str-Strip-Control](@TempID)

            EXEC ('ALTER TABLE #LoadTable ADD  [' +  @StripID + '] nVarChar(max)')

            DELETE  FROM #ColumnNames WHERE value = @TempID

     END

      -- Remove this column or the bulk insert will fail.
    ALTER TABLE #LoadTable DROP COLUMN DUMMY
    
    -- Upload the rest of the table
    EXEC( 'BULK INSERT #LoadTable   FROM ''' + @pURL + ''' WITH (FORMAT = ''CSV'', FIRSTROW = 2, ROWTERMINATOR = ''\n'')')

    IF(OBJECT_ID('tempdb..#ColumnNames')) IS NOT NULL  DROP TABLE #ColumnNames

    IF(OBJECT_ID('tempdb..#FirstRowHold')) IS NOT NULL DROP TABLE #FirstRowHold



END

#LoadTable is a temp table so there is no conflict if multiple users run this proc at the same time. I also did not have to use openrowset or system tables to reconstruct the table since its already done when the calling proc gets control back from the FetchExcelSheet.

Only thing to remember is to create the #LoadTable in the parent each time you create a new one. Other than that this has been working great for us.

Girvin answered 7/12, 2023 at 19:10 Comment(0)
A
-3
  1. First select an empty result set into the temptable, just to have it created.
  2. Run the exec SP into TABLE
SELECT TOP 0 * INTO [temp_table] FROM [Table]; 

EXEC [StoredProcedure] INTO [temp_table];
Andino answered 14/2, 2022 at 9:1 Comment(1)
This is not valid T-SQL! SQL Server supports only the INSERT INTO ... EXECUTE ... syntax.Beasley
S
-5

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'

Shiprigged answered 19/8, 2014 at 19:12 Comment(1)
There could be some obstaclesto make your first step. By example if the the original SP use temporary tables. UDFs can not use temporary tables.Calc

© 2022 - 2024 — McMap. All rights reserved.