1

I want to insert the results of a stored procedure into a temp table using OPENROWSET. However, the issue I run into is I'm not able to pass parameters to my stored procedure.

This is my stored procedure:

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[N_spRetrieveStatement]
    @PeopleCodeId nvarchar(10),
    @StatementNumber int
AS
    SET NOCOUNT ON

    DECLARE @PersonId int
    SELECT @PersonId = [dbo].[fnGetPersonId](@PeopleCodeId)

    SELECT * 
    INTO #tempSpRetrieveStatement 
    FROM OPENROWSET('SQLNCLI', 'Server=PCPRODDB01;Trusted_Connection=yes;',
                    'EXEC Campus.dbo.spRetrieveStatement @StatementNumber, @PersonId');
     
    --2577, 15084

    SELECT * 
    FROM #tempSpRetrieveStatement;
Dale K
  • 25,246
  • 15
  • 42
  • 71
JCastillo
  • 336
  • 3
  • 13
  • 1
    Do you have a linked server? Why not `EXEC PCPRODDB01.Campus.dbo.spRetrieveStatement @StatementNumber, @PersonId;`? – Aaron Bertrand Jan 12 '22 at 19:38
  • Does the insert in the temp table actually have a point? Is something going to be happening between the `SELECT ... INTO` and `SELECT .. FROM` that warrants an intermediate table that you left out for simplicity? First selecting into a temp table and then selecting the contents of that is obviously needlessly roundabout if not. – Jeroen Mostert Jan 12 '22 at 19:49
  • @JeroenMostert Yes, I do need to insert the results into a temp table. it will later be used for other purpose. – JCastillo Jan 12 '22 at 20:07
  • See https://stackoverflow.com/questions/13831472/using-a-variable-in-openrowset-query – tinazmu Jan 12 '22 at 20:55
  • Does this answer your question? [Using a Variable in OPENROWSET Query](https://stackoverflow.com/questions/13831472/using-a-variable-in-openrowset-query) – Charlieface Jan 13 '22 at 01:35
  • Using dynamic sql did not work the way I expected, the temp table `#tempSpRetrieveStatemet` does not get created. – JCastillo Jan 13 '22 at 13:21

2 Answers2

0

OpenRowSet will not allow you to execute Procedure with input parameters. You have to use INSERT/EXEC.

INTO #tempSpRetrieveStatement(Col1, Col2,...)
EXEC PCPRODDB01.Campus.dbo.spRetrieveStatement @StatementNumber, @PersonId

Create and test a LinkedServer for PCPRODDB01 before running the above command.

JERRY
  • 1,165
  • 1
  • 8
  • 22
  • Yeah, this is my other option. The problem with this is that if the columns in the stored procedure change it will cause issues with the temp table columns as well. – JCastillo Jan 13 '22 at 13:26
0

The root of your problem is that you don't actually have parameters inside your statement that you're transmitting to the remote server you're connecting to, given the code sample you provided. Even if it was the very same machine you were connecting to, they'd be in different processes, and the other process doesn't have access to your session variables.

LinkedServer was mentioned as an option, and my understanding is that's the preferred option. However in practice that's not always available due to local quirks in tech or organizational constraints. It happens.

But there is a way to do this.

It's hiding in plain sight.

You need to pass literals into the string that will be executed on the other server, right?

So, you start by building the string that will do that.

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[N_spRetrieveStatement]
    @PeopleCodeId nvarchar(10),
    @StatementNumber int
AS

SET NOCOUNT ON

DECLARE
    @PersonId INT,
    @TempSQL VARCHAR(4000) = '';

SELECT @PersonId = [dbo].[fnGetPersonId](@PeopleCodeId);


SET @TempSQL =
    'EXEC Campus.dbo.spRetrieveStatement(''''' + 
        FORMAT(@StatementNumber,'D') +''''', ''''' +
        FORMAT(@PersonId,'D') + ''''')';
        --2577, 15084

Note the seemingly excessive number of quotes. That's not a mistake -- that's foreshadowing. Because, yes, OPENROWSET hates taking variables as parameters. It, too, only wants literals. So, how do we give OPENROWSET what it needs?

We create a string that is the entire statement, no variables of any kind. And we execute that.

SET @TempSQL = 
    'SELECT * INTO #tempSpRetrieveStatement ' +
    'FROM OPENROWSET(''SQLNCLI'', ''Server=PCPRODDB01;Trusted_Connection=yes;'', ' + @TempSQL + 
                    'EXEC Campus.dbo.spRetrieveStatement @StatementNumber, @PersonId';

EXEC (@TempSQL);

SELECT * 
FROM #tempSpRetrieveStatement;

And that's it! Pretty simple except for counting your escaped quotes, right?

Now... This is almost beyond the scope of the question you asked, but it is a 'gotcha' I've experienced in executing stored procedures in another machine via OPENROWSET. You're obviously used to using temp tables. This will fail if the stored procedure you're calling is creating temp tables or doing a few other things that -- in a nutshell -- inspire the terror of ambiguity into your SQL server. It doesn't like ambiguity. If that's the case, you'll see a message like this:

"Msg 11514, Level 16, State 1, Procedure sp_describe_first_result_set, Line 1

The metadata could not be determined because statement '…your remote EXEC statement here…' in procedure '…name of your local stored procedure here…' contains dynamic SQL. Consider using the WITH RESULT SETS clause to explicitly describe the result set."

So, what's up with that?

You don't just get data back with OPENROWSET. The local and remote servers have a short conversation about what exactly the local server is going to expect from the remote server (so it can optimize receiving and processing it as it comes in -- something that's extremely important for large rowsets). Starting with SQL Server 2012, sp_describe_first_result_set is the newer procedure for this, and normally it executes quickly without you noticing it. It's just that it's powers of divination aren't unlimited. Namely, it doesn't know how to get the type and name information regarding temp tables (and probably a few other things it can't do -- PIVOT in a select statement is probably right out).

I specifically wanted to be sure to point this out because of your reply regarding your hesitation about using LinkedServer. In fact, the very same reasons you're hesitant are likely to render that error message's suggestion completely useless -- you can't even predict what columns you're getting and in what order until you've got them.

I think what you're doing will work if, say, you're just branching upstream based on conditional statements and are executing one of several potential SELECT statements. I think it will work if you're just not confident that you can depend on the upstream component being fixed and are trying to ensure that even if it varies, this procedure doesn't have to because it's very generic.

But on the other hand you're facing a situation in which you literally cannot guarantee that SQL Server can predict the columns, you're likely going to have to force some changes in the stored procedure you're calling to insist that it's stable. You might, for instance work out how to ensure all possible fields are always present by using CASE expressions rather than any PIVOT. You might create a session table that's dedicated to housing what you need to SELECT just long enough to do that then DELETE the contents back out of there. You might change the way in which you transmit your data such that it's basically gone through the equivalent of UNPIVOT. And after all that extra work, maybe it'll be just a matter of preference if you use LinkedServer or OPENROWSET to port the data across.

So that's the answer to the literal question you asked, and one of the limits on what you can do with the answer.

Dharman
  • 30,962
  • 25
  • 85
  • 135