436

I have a stored procedure that returns rows:

CREATE PROCEDURE MyProc
AS
BEGIN
    SELECT * FROM MyTable
END

My actual procedure is a little more complicated, which is why a stored procedure is necessary.

Is it possible to select the output by calling this procedure?

Something like:

SELECT * FROM (EXEC MyProc) AS TEMP

I need to use SELECT TOP X, ROW_NUMBER, and an additional WHERE clause to page my data, and I don't really want to pass these values as parameters.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
jonathanpeppers
  • 26,115
  • 21
  • 99
  • 182
  • I'm unsure as to what you intend to do here because when you execute the procedure, you are getting the rows back. Is it that you want to execute the procedure inside a SELECT statement so you can tie it to a pageable object? – Raj More Sep 29 '09 at 13:11
  • 1
    Is there a particular reason why you don't want to pass the values as parameters? To do it the way you are suggesting is a bit inefficent - you would be selecting more data than you need, and then not using it all. – Mark Bell Sep 29 '09 at 13:13
  • 2
    Take a look at here: http://www.sommarskog.se/share_data.html – pylover May 09 '12 at 10:51
  • 2
    If anyone wants to insert the sp output into a TABLE variable have a lot of columns in your sp, press `Ctrl+T` to output the result as text, and copy the first column line and remove extra whitespaces from there and you'll get your column names easily. To go back to Grid output press `Ctrl+D` – Ashraful Aug 30 '20 at 12:12
  • 11
    **Beware all ye who enter here:** There are only about three answers provided below atm: **1.** use a function (accepted answer), **2.** use a table variable or temp table and `INSERT` the `EXEC` [***iff*** your sproc isn't too complicated](https://stackoverflow.com/questions/1492411#comment24260611_1492432), **3.** [cheat and use `OPENQUERY`](https://stackoverflow.com/a/1492502/1028230). But each answer is repeated an amazing number of times, many with a downright startling number of upvotes. You've been warned. – ruffin Jul 14 '21 at 18:56
  • 1
    @ruffin naturally I found out the truth of which you speak the hard way before finally finding your comment. – egmfrs May 23 '23 at 12:11
  • I've added the answer I came here looking for, enticed by the title, even if it's not exactly what the conditions of the OP required (needing to use TOP, WHERE, etc.) In summary: `MyProcedure 'Hello', 1` – egmfrs May 23 '23 at 12:32

16 Answers16

285

You can

  1. create a table variable to hold the result set from the stored proc and then
  2. insert the output of the stored proc into the table variable, and then
  3. use the table variable exactly as you would any other table...

... sql ....

Declare @T Table ([column definitions here])
Insert @T Exec storedProcname params 
Select * from @T Where ...
Charles Bretana
  • 143,358
  • 22
  • 150
  • 216
  • 60
    The problem with `INSERT #T` or `INSERT @T` is that an `INSERT EXEC` statement cannot be nested. If the stored procedure already has an `INSERT EXEC` in it, this won't work. – MOHCTP May 30 '13 at 01:44
  • 2
    This probably the most portable solution, being closest to basic SQL. It also helps to maintain strong column type definitions. Should have more upvotes than those above. –  Aug 08 '14 at 18:58
  • The [table variables](https://www.simple-talk.com/sql/t-sql-programming/temporary-tables-in-sql-server/) looks more useful here than temporary tables in terms of sp recompile. So I agree, this answer should have more upvotes. – resnyanskiy Mar 16 '16 at 05:21
181

You can use a User-defined function or a view instead of a procedure.

A procedure can return multiple result sets, each with its own schema. It's not suitable for using in a SELECT statement.

Kols
  • 3,641
  • 2
  • 34
  • 42
Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • 8
    Additionally, if after converting to a UDF you find you need the stored procedure semantics you can always wrap the UDF with a procedure. – Joel Coehoorn Sep 29 '09 at 13:26
  • what if, we need to send parameters to mulple stored procedures and combine them into one one big stored procedure? Can view, take parameters, like stored procedures does – mrN Aug 18 '11 at 07:14
  • 3
    @mrN Views don't take parameters, but UDFs do. – Mehrdad Afshari Aug 18 '11 at 08:26
  • 3
    Hello, I really need to do this without converting the sp to a view or function, is it possible? – luisgepeto Mar 07 '17 at 17:46
  • @LuisBecerril Same here. The underlying sproc is protected and I have no permission to change it (or even view the script) – jf328 Jul 10 '17 at 21:39
  • 3
    While your answer is a true statement it doesn't answer the question.... "SELECT FROM stored procedure" Which sure it's not ideal, but it is what it is... @Aamir's answer is the correct answer. Either that or the question needs to be changed... which seems a bit ridiculous to me. – Urasquirrel Jun 17 '19 at 22:42
95

You either want a Table-Valued function or insert your EXEC into a temporary table:

INSERT INTO #tab EXEC MyProc
CMerat
  • 4,398
  • 25
  • 28
  • 54
    The problem with `INSERT #T` or `INSERT @T` is that an `INSERT EXEC` statement cannot be nested. If the stored procedure already has an `INSERT EXEC` in it, this won't work. – MOHCTP May 30 '13 at 01:44
73

You need to declare a table type which contains the same number of columns your store procedure is returning. Data types of the columns in the table type and the columns returned by the procedures should be same

declare @MyTableType as table
(
FIRSTCOLUMN int
,.....
)  

Then you need to insert the result of your stored procedure in your table type you just defined

Insert into @MyTableType 
EXEC [dbo].[MyStoredProcedure]

In the end just select from your table type

Select * from @MyTableType
Aamir
  • 1,832
  • 14
  • 14
  • That is the best solution for me, because you don't need to specify the server name, connection strings or have to configure any linked servers in order to make it work - which are things I don't want to do to just to get some data back. Thank you! Awsome answer! – Matt Dec 05 '17 at 09:27
  • When stored procedure is too dificult - this method not workin, for example, when stored procedure use two temp-tables. – nick_n_a Dec 27 '18 at 09:25
  • 1
    It works only inside stored procedure. In query console (as a separate query) it causes an error: "An INSERT EXEC statement cannot be nested.". – Kate Sep 28 '22 at 07:26
55

You must read about OPENROWSET and OPENQUERY

SELECT  * 
INTO    #tmp FROM    
OPENQUERY(YOURSERVERNAME, 'EXEC MyProc @parameters')
Paul
  • 4,160
  • 3
  • 30
  • 56
Rizwan Mumtaz
  • 3,875
  • 2
  • 30
  • 31
38

It is not necessary use a temporary table.

This is my solution

SELECT  *  FROM    
OPENQUERY(YOURSERVERNAME, 'EXEC MyProc @parameters')
WHERE somefield = anyvalue
slavoo
  • 5,798
  • 64
  • 37
  • 39
DavideDM
  • 1,475
  • 16
  • 18
  • 3
    This needs you to add your server as a linked server to itself, but it works like a charm! thanks! – vaheeds Nov 16 '16 at 09:42
  • 1
    Some great caveats on this: http://stackoverflow.com/questions/2374741/why-is-using-openquery-on-a-local-server-bad – Keith Adler May 19 '17 at 07:57
  • 1
    Hmm ... I am getting the error "Error 7411: Server 'YourServerName' is not configured for DATA ACCESS." What do I need to change? – Matt Dec 05 '17 at 09:21
  • Have you add your server as a linked server? YourServerName is the name of your server. You have to change YourServerName with your real server name. – DavideDM Dec 05 '17 at 10:50
  • @Matt: `sp_serveroption 'MYSERVER', 'DATA ACCESS', TRUE;` – alexkovelsky Sep 10 '18 at 16:25
  • @alexkovelsky - Thanks, calling `EXEC sp_serveroption 'MYSERVER', 'DATA ACCESS', TRUE;` indeed makes the error disappear. But now I am getting a syntax error when I execute `SELECT * FROM OPENQUERY(YOURSERVERNAME, 'EXEC MyProc @parameters')` - even a pass-through query like `SELECT * FROM OPENQUERY (Server, 'SELECT name FROM dbo.Customers`);` as mentioned [here in Section D.](https://learn.microsoft.com/en-us/sql/t-sql/functions/openquery-transact-sql?view=sql-server-2017) gives me a syntax error. What else is missing? – Matt Sep 11 '18 at 06:45
  • @vaheeds `You cannot create a local SQL Server as a linked server` How to get past that error ? – GuidoG Nov 20 '20 at 13:01
30

You can copy output from sp to temporaty table.

CREATE TABLE #GetVersionValues
(
    [Index] int,
    [Name]  sysname,
    Internal_value  int,
    Character_Value sysname
)
INSERT #GetVersionValues EXEC master.dbo.xp_msver 'WindowsVersion'
SELECT * FROM #GetVersionValues
drop TABLE #GetVersionValues
8

Try converting your procedure in to an Inline Function which returns a table as follows:

CREATE FUNCTION MyProc()
RETURNS TABLE AS
RETURN (SELECT * FROM MyTable)

And then you can call it as

SELECT * FROM MyProc()

You also have the option of passing parameters to the function as follows:

CREATE FUNCTION FuncName (@para1 para1_type, @para2 para2_type , ... ) 

And call it

SELECT * FROM FuncName ( @para1 , @para2 )
Phillip Senn
  • 46,771
  • 90
  • 257
  • 373
al_the_man
  • 295
  • 4
  • 6
8

If 'DATA ACCESS' false,

EXEC sp_serveroption 'SQLSERVERNAME', 'DATA ACCESS', TRUE

after,

SELECT  *  FROM OPENQUERY(SQLSERVERNAME, 'EXEC DBNAME..MyProc @parameters')

it works.

Ali Osman Yavuz
  • 389
  • 3
  • 4
7

You can cheat a little with OPENROWSET :

SELECT ...fieldlist...
FROM OPENROWSET('SQLNCLI', 'connection string', 'name of sp')
WHERE ...

This would still run the entire SP every time, of course.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
MartW
  • 12,348
  • 3
  • 44
  • 68
  • 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. For more information about enabling 'Ad Hoc Distributed Queries', search for 'Ad Hoc Distributed Queries' in SQL Server Books Online. – GuidoG Nov 20 '20 at 12:51
7

Use OPENQUERY, and before execute set SET FMTONLY OFF; SET NOCOUNT ON;

Try this sample code:

SELECT top(1)*
FROM
OPENQUERY( [Server], 'SET FMTONLY OFF; SET NOCOUNT ON; EXECUTE  [database].[dbo].[storedprocedure]  value,value ')

If you get the error 'Server is not configured for DATA ACCESS', use this:

EXEC sp_serveroption 'YourServer', 'DATA ACCESS', TRUE
TT.
  • 15,774
  • 6
  • 47
  • 88
4

For the sake of simplicity and to make it re-runnable, I have used a system StoredProcedure "sp_readerrorlog" to get data:

-----USING Table Variable
DECLARE @tblVar TABLE (
   LogDate DATETIME,
   ProcessInfo NVARCHAR(MAX),
   [Text] NVARCHAR(MAX)
)
INSERT INTO @tblVar Exec sp_readerrorlog
SELECT LogDate as DateOccured, ProcessInfo as pInfo, [Text] as Message FROM @tblVar



-----(OR): Using Temp Table
IF OBJECT_ID('tempdb..#temp') IS NOT NULL  DROP TABLE #temp;
CREATE TABLE #temp (
   LogDate DATETIME,
   ProcessInfo NVARCHAR(55),
   Text NVARCHAR(MAX)
)
INSERT INTO #temp EXEC sp_readerrorlog
SELECT * FROM #temp
KirstieBallance
  • 1,238
  • 12
  • 26
Sheikh Kawser
  • 136
  • 2
  • 6
2

It sounds like you might just need to use a view. A view allows a query to be represented as a table so it, the view, can be queried.

Lawrence Barsanti
  • 31,929
  • 10
  • 46
  • 68
1

If your server is called SERVERX for example, this is how I did it...

EXEC sp_serveroption 'SERVERX', 'DATA ACCESS', TRUE;
DECLARE @CMD VARCHAR(1000);
DECLARE @StudentID CHAR(10);
SET @StudentID = 'STUDENT01';
SET @CMD = 'SELECT * FROM OPENQUERY([SERVERX], ''SET FMTONLY OFF; SET NOCOUNT ON; EXECUTE MYDATABASE.dbo.MYSTOREDPROC ' + @StudentID + ''') WHERE SOMEFIELD = SOMEVALUE';
EXEC (@CMD);

To check this worked, I commented out the EXEC() command line and replaced it with SELECT @CMD to review the command before trying to execute it! That was to make sure all the correct number of single-quotes were in the right place. :-)

I hope that helps someone.

Fandango68
  • 4,461
  • 4
  • 39
  • 74
1

I find this usefull. Real example.

declare @tempv1 table (Number int, AccountType varchar(255), DisplayName varchar(255), Kind int, UsagePageBreak int, PrimoPrev money, PrevPeriod money, UltimoPrev money, Primo money, Debit money, Credit money, Period money, Ultimo money, ToCurrentDate money, IndexUltimoPct money, IndexPeriodPct money, UltimoPrevOK int, UltimoOK int)
declare @tempv2 table (Number int, AccountType varchar(255), DisplayName varchar(255), Kind int, UsagePageBreak int, PrimoPrev money, PrevPeriod money, UltimoPrev money, Primo money, Debit money, Credit money, Period money, Ultimo money, ToCurrentDate money, IndexUltimoPct money, IndexPeriodPct money, UltimoPrevOK int, UltimoOK int)


insert into @tempv1

exec  sp_reports_Accounting_BalanceV2 
@fromaccount=1010,
@toaccount=1010,
@fromfinancialdate = '2021-01-01 00:00:00 +01:00', 
@tofinancialdate = '2021-12-31 00:00:00+01:00', 
@includezero=0,@ouids=NULL,@currentOrganizationalUnitId=1,@currentuserid=1,@includenotbooked=0


insert into @tempv2

exec sp_reports_Accounting_BalanceV3
@fromaccount=1010,
@toaccount=1010,
@fromfinancialdate = '2021-01-01 00:00:00 +01:00', 
@tofinancialdate = '2021-12-31 00:00:00+01:00', 
@includezero=0,@ouids=NULL,@currentOrganizationalUnitId=1,@currentuserid=1,@includenotbooked=0


select * from @tempv1 except select * from @tempv2
union all
select * from @tempv2 except select * from @tempv1
0

Several hours and 131 google searches later, turns out all I really needed was:

MyProcedure -- this is so simple it doesn't look like an executable line of code so here's a comment to make it look more substantial

(no EXEC, no SELECT, no brackets...)

Hit F5 / Run - and you shall have your table of results.

So, to clarify: just put the procedure name - if you need to put in parameters, put a space and then the parameters you wish to pass in, separated by a comma if you have multiple.

Example with parameters:

MyProcedure 'Hello', 1

(Single quotations needed for varchar; no quotes for bit and integer.)

egmfrs
  • 1,272
  • 3
  • 17
  • 31