Nested sp_executesql not working with output variable - sql-server

I am trying to call a stored procedure (with output variable) using sp_executesql but within another stored procedure. I wrote the following, but still not able to get trhough what that error means
This would be called from webservice code:
exec sp1 'obj1',#params
Here obj and params are of nvarchar(max)
Definition of sp1 is :
Alter procedure [dbo].[sp1 ]
#procname nvarchar(max),
#params nvarchar(max)
as
declare #temp varchar(15)
if #procname = 'obj1'
begin
set #params = #params + ',#Newval varchar(15) output'
EXEC sp_executesql #sp2,#params,#Newval=#temp OUTPUT
end
Definition of sp2:
Here I am returning #Newval
Error I am getting :
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near ','.
Also in 2 in place of exec statement , I have tried following:
EXEC sp_executesql #sp2, #params, #temp OUTPUT;
Results in the same error.
set #sql='sp2,' + ' #params ' + ',#params,#temp OUTPUT'
EXEC sp_executesql (#sql)
Also results in the same error.
I need this dynamic selection of stored procedures in sp1 and params is a nvarchar(max) string of parameters and their values, some of them are varchar and are embedded in ''value'' format but this string is fine as I am able to call the underlying sp2 with this.
Additional info, it it helps.
EXEC sp_executesql #sp2,#params,#Newval=#temp OUTPUT
in this #params is combination of keys and vlaue pairs for the final sp. something like :
'#key1="a",#key2="b"'
and so on, I can not predefined the #params but it is dynamic and it is working
fine when I run it with
exec (#sql)
Format while whole of the name, params are embedded in the #sql

If #params='' or NULL then your , before #Newval is irrelevant. I suggest you to check:
IF NULLIF(#params,'') IS NULL or #params IS NULL
SET #params = '#Newval varchar(15) output'
ELSE
SET #params = #params + ',#Newval varchar(15) output'
You are passing #sp2 maybe you need this:
ALTER PROCEDURE [dbo].[sp1]
#procname nvarchar(max),
#params nvarchar(max)
AS
DECLARE #temp varchar(15)
IF #procname = 'obj1'
BEGIN
SET #params = #params + ',#Newval varchar(15) output'
EXEC sp_executesql N'EXEC sp2 #someparam1, #someparam2, #Newval varchar(15) OUTPUT', #params, #someparam1 = 1, #someparam2 = 2, #Newval=#temp OUTPUT
END
EDIT
Working example:
USE [AdventureWorks]
GO
DECLARE #procname nvarchar(max) = 'EXEC [dbo].[uspGetWhereUsedProductID] #StartProductID, #CheckDate',
#params nvarchar(max) = '#StartProductID int, #CheckDate date'
EXEC sp_executesql #procname, #params, #StartProductID = 1, #CheckDate = '2015-10-17'
GO

Related

Must Declare Scalar variable #nRuleId

USE GDMDBNS_1720
GO
IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE = 'PROCEDURE'
AND ROUTINE_SCHEMA = 'dbo' AND ROUTINE_NAME = 'usp_RmsExecuteValidationRule')
BEGIN
DROP PROCEDURE dbo.usp_RmsExecuteValidationRule
END
GO
Create PROCEDURE [dbo].[usp_RmsExecuteValidationRule]
#nRuleId INT,
#FIPSName VARCHAR(5)
AS
BEGIN
DECLARE #strRuleQuery VARCHAR(MAX)
DECLARE #sqlstat AS NVARCHAR(MAX)
DECLARE #params AS NVARCHAR(MAX)
SET #strRuleQuery = N'SELECT RULE_QUERY from GdmValidationRuleMaster where
RULE_ID = #nRuleId'
SET #sqlstat = #strRuleQuery
SET #params = N'#FIPSName VARCHAR(5)'
EXEC sp_executesql #params,
#query = #sqlstat,
#FIPSName = #FIPSName
END
got error while Debugging must declare scalar variable #nRuleId . I have already declared. But error is pooping out always.
There appear to be a number of issues with the code, as is.
To return a value, you need to declare an OUTPUT parameter and the variable #FIPSName is not actually used in the code, and #strRuleQuery is superfluous in this code.
Here is the code from inside the SProc, that should be closer to your needs.
-- Parameters
DECLARE
#nRuleId INT = 1,
#FIPSName VARCHAR(5) = 'ITS'
-- Local variables
DECLARE #sqlstat AS NVARCHAR(MAX)
DECLARE #params AS NVARCHAR(MAX)
DECLARE #RULE_QUERY NVARCHAR(MAX)
SET #sqlstat = N'SELECT #RULE_QUERY=RULE_QUERY from GdmValidationRuleMaster where RULE_ID = #nRuleId'
SET #params = N'#nRuleId INT, #RULE_QUERY NVARCHAR(MAX) OUTPUT'
EXEC sp_executesql
#sqlstat,
#params,
#nRuleId = #nRuleId, #RULE_QUERY= #RULE_QUERY OUTPUT
-- Output
SELECT #RULE_QUERY
You must rewrite execution part:
SET #params = N'#FIPSName VARCHAR(5), #nRuleId int'
EXEC sp_executesql #strRuleQuery, #params, #FIPSName = #FIPSName, #nRuleId = #nRuleId
I don't understand, why do you pass #FIPSName as parameter, it is not used in your batch.

save result of exec sp_excutequery using xpath

Hi i have this example
DECLARE
#XML1 xml,
#XPath nvarchar(200),
#EncodedXPath nvarchar(200),
#Sql nvarchar(max),
#val nvarchar(20)
SET #XML1='
<Root>
<Device>
<Inspection>
<Status>OK</Status>
</Inspection>
</Device>
</Root>'
SELECT #XML1.query('/Root[1]/Device[1]/Inspection[1]/Status[1]')
SELECT #XML1.value('/Root[1]/Device[1]/Inspection[1]/Status[1]','varchar(5)')
SET #XPath = '/Root[1]/Device[1]/Inspection[1]/Status[1]'
SET #EncodedXPath = REPLACE(#XPath, '''', '''''')
SET #Sql = N'SELECT #XML1.query(''' + #EncodedXPath + N''')'
EXEC sp_executesql #Sql, N'#XML1 xml', #XML1
SET #Sql = N'SELECT #XML1.value(''' + #EncodedXPath + N''', ''varchar(5)'')'
EXEC sp_executesql #Sql, N'#XML1 xml', #XML1
if you execute the code above you get same result, but how can i assign the result of the dynamic sql to a variable using xpath? the below example return just the result of the execution but i want to get back the value 'OK'
EXEC #ret = sp_executesql #Sql, N'#XML1 xml', #XML1
The approach should be the same as getting result of any sp_executesql into a variable, no matter the sp_executesql contains XPath or just plain SQL.
This is one possible way, which also suggested in "How to get sp_executesql result into a variable?". Modify your dynamic SQL to have an output variable for storing the result of the XPath query. Then you can set your static variable -the one declared outside sp_executesql- from the output variable value :
....
SET #Sql = N'SET #ret = #XML1.value(''' + #EncodedXPath + N''', ''varchar(5)'')'
EXEC sp_executesql #Sql, N'#XML1 xml, #ret varchar(5) output', #XML1, #ret = #ret output

SQL server 2012 - return variable value from a dynamic script

I'm trying to run a dynamic script to then return the variable so I can pass in to the rest of my script. I've a couple of ways with the help of Google, but I think I still haven't got the syntax correct, therefore getting error or null value returned.
Can someone please advise where I've gone wrong.
For example:
To return the value for variable #table_name
ASIA is the database and this is set as variable which is appended to the table name that is retrieved from the table and T5148 is the id from the table to turn the table name as is so a variable. I have set this a variables as this script sits when other scripts which loops
Thank you
declare #table_name nvarchar(50)
declare #database nvarchar(50)
declare #id nvarchar(50)
declare #sql nvarchar(max)
set #database = 'ASIA'
set #id = 'T5178'
set #sql = N'SELECT #table_name = ''#database''+table_name
FROM ''#database''+tables (NOLOCK)
WHERE id = ''#id'''
exec sp_executesql #sql, N'#table_name nvarchar(50) output', #table_name output
select #TRAN_TABLE
If I am not wrong, this is what you need :
DECLARE #table_name NVARCHAR(50)
DECLARE #database NVARCHAR(50)
DECLARE #id NVARCHAR(50)
DECLARE #sql NVARCHAR(MAX)
SET #database = 'ASIA'
SET #id = 'T5178'
SET #sql = N'SELECT #table_name = table_name
FROM ' + #database+'.dbo.tables (NOLOCK)
WHERE id = #id'
EXEC SP_EXECUTESQL #sql, N'#id nvarchar(50),#table_name nvarchar(50) output',
#id = #id, #table_name = #table_name OUTPUT
SET #table_name = #database + #table_name
Note : change dbo as you schema name.

Assigning Dynamic SQL Output Variables to Local Variables

Setting aside the caveats of using dynamic SQL, I am wondering whether I can dynamically declare which local variable I want to assign the value of a given output variable from a dynamic SQL statement.
I hope I said that well enough. Here's a little bit of the code and where I'm having trouble. I have a stored procedure with a number of OUTPUT parameters, like so:
ALTER PROCEDURE [dbo].[mngi_psi_paginate]
#image_desc_1 VARCHAR(50) OUTPUT
,#image_desc_2 VARCHAR(50) OUTPUT
,#image_desc_3 VARCHAR(50) OUTPUT
,#image_desc_4 VARCHAR(50) OUTPUT
,#image_desc_5 VARCHAR(50) OUTPUT
,#image_desc_6 VARCHAR(50) OUTPUT
,#image_desc_7 VARCHAR(50) OUTPUT
,#image_desc_8 VARCHAR(50) OUTPUT
,#image_desc_9 VARCHAR(50) OUTPUT
,#image_desc_10 VARCHAR(50) OUTPUT
...
There are a total of 40 OUTPUT parameters. I will be allowing the user to page through an unknown number of images, 10 images at a time. For each image, I need 4 fields of data, but with a potential of 10 records at a time, I'd rather not hardcode all 40 variable assignments.
I realize that there should be better ways to do this, but the receiving application is not a web browser, so unfortunately, this is what I have to work with.
I also do a few other things: determine the total number of images, the number of pages, and the number of images that will be displayed on the page.
Once I figure out which set of images belong on the given page, I put them into a temporary table.
CREATE TABLE #images_page
(primary_key INT IDENTITY(1,1) NOT NULL
,image_id VARCHAR(36)
,image_path VARCHAR(400)
,image_type VARCHAR(5)
,image_desc VARCHAR(MAX)
)
INSERT INTO #images_page
SELECT image_id
,image_path
,image_type
,image_desc
FROM #images_set
WHERE primary_key >= #image_start
AND primary_key <= #image_end
Then I build a string to execute a dynamic SQL statement, which is all wrapped in a WHILE loop, with the hope of being able to assign only the variables that have a corresponding record (e.g., #image_desc_1 gets data from row #1, #image_desc_2 gets data from row #, etc.
SELECT #image_page_total = COUNT(*)
FROM #images_page
DECLARE #row_counter INT
,#loop_counter INT
SET #loop_counter = #image_page_total
SET #row_counter = 1
WHILE
#loop_counter > 0
AND #row_counter <= #loop_counter
BEGIN
-- Dynamically assign variables
DECLARE #sql NVARCHAR(MAX)
DECLARE #params NVARCHAR(MAX)
DECLARE #assign NVARCHAR(MAX)
SELECT #sql = 'SELECT #image_desc = image_desc ' +
'#image_path = image_path,' +
'#image_type = image_type,' +
'#image_comments = image_comments' +
'FROM #images_page ' +
'WHERE primary_key = ' + CAST(#row_counter AS VARCHAR)
SELECT #params = '#image_desc varchar(50) OUTPUT'
SELECT #assign = '#image_desc = #image_desc_' + CAST(#row_counter AS VARCHAR) + ' OUTPUT'
--This line works because I've simply typed out the variable name
EXEC sp_executesql #sql, #params, #image_desc = #image_desc_1 OUTPUT
--This line does not work because I am trying to append the '_1' to #image_desc
EXEC sp_executesql #sql, #params, #assign
SET #row_counter = #row_counter + 1
END
The first EXEC sp_executesql line works because that's the way it's supposed to work and that's the way you see it in all of the documentation and examples. The second EXEC sp_executesql is what I'd really like to be able to do, but I just can't seem to see the way to do it.
Is there a way to accomplish this the way I am trying to do it?
I am open to alternative methods that would not require me to hardcode the variable assignments.
Thank you in advance for any assistance!
I don't think what you need is entirely possible, but perhaps this will guide you in the right direction. It doesn't entirely remove the deed to reference all 40 variable is code, but you don't have to hardcode the variable assignment.
I've used my own example. I've setting the value of #T1 or #T2 depending on the value of #I. So if #I = 1, then #T1 will be set to 'SET AT RUNTIME'. If #I = 2, then #T2 will be set to 'SET AT RUNTIME'. You would need to pass in all 40 variables to your dynamic query.
DECLARE #T1 INT,
#T2 INT,
#I VARCHAR(10) = '2',
#SQL NVARCHAR(MAX);
SET #SQL = 'SET #T' + #I + ' = ''SET AT RUNTIME'''
EXEC sp_executesql #SQL, N'#T1 INT OUT, #T2 INT OUT', #T1 OUT, #T2 OUT
SELECT #T1, #T2
Hope that helps
It seems that Spock is correct and I can't dynamically compute the variable that I want to pass out from the dynamic SQL. Someone please correct me, if I'm wrong.
Since I have just the 10 sets of variables to deal with, I sucked it up and just wrote an IF statement for each one. Again, if someone has a better alternative, I am open to anything.
For the record, here is what I did and this works:
SET #loop_counter = #image_page_total
SET #row_counter = 1
WHILE
#loop_counter > 0
AND #row_counter <= #loop_counter
BEGIN
-- Dynamically assign variables
DECLARE #sql NVARCHAR(MAX)
DECLARE #params NVARCHAR(MAX)
DECLARE #assign NVARCHAR(MAX)
SELECT #sql = 'SELECT #image_path = image_path, ' +
'#image_type = image_type, ' +
'#image_desc = image_desc, ' +
'#image_comments = image_comments ' +
'FROM #images_page ' +
'WHERE primary_key = ' + CAST(#row_counter AS VARCHAR)
SELECT #params = '#image_path VARCHAR(400) OUTPUT, #image_type VARCHAR(5) OUTPUT, #image_desc VARCHAR(50) OUTPUT, #image_comments VARCHAR(MAX) OUTPUT'
IF #row_counter = 1 EXEC sp_executesql #sql, #params, #image_type_1 OUTPUT, #image_type_1 OUTPUT, #image_desc_1 OUTPUT, #image_comments_1 OUTPUT
IF #row_counter = 2 EXEC sp_executesql #sql, #params, #image_type_2 OUTPUT, #image_type_2 OUTPUT, #image_desc_2 OUTPUT, #image_comments_2 OUTPUT
IF #row_counter = 3 EXEC sp_executesql #sql, #params, #image_type_3 OUTPUT, #image_type_3 OUTPUT, #image_desc_3 OUTPUT, #image_comments_3 OUTPUT
IF #row_counter = 4 EXEC sp_executesql #sql, #params, #image_type_4 OUTPUT, #image_type_4 OUTPUT, #image_desc_4 OUTPUT, #image_comments_4 OUTPUT
IF #row_counter = 5 EXEC sp_executesql #sql, #params, #image_type_5 OUTPUT, #image_type_5 OUTPUT, #image_desc_5 OUTPUT, #image_comments_5 OUTPUT
IF #row_counter = 6 EXEC sp_executesql #sql, #params, #image_type_6 OUTPUT, #image_type_6 OUTPUT, #image_desc_6 OUTPUT, #image_comments_6 OUTPUT
IF #row_counter = 7 EXEC sp_executesql #sql, #params, #image_type_7 OUTPUT, #image_type_7 OUTPUT, #image_desc_7 OUTPUT, #image_comments_7 OUTPUT
IF #row_counter = 8 EXEC sp_executesql #sql, #params, #image_type_8 OUTPUT, #image_type_8 OUTPUT, #image_desc_8 OUTPUT, #image_comments_8 OUTPUT
IF #row_counter = 9 EXEC sp_executesql #sql, #params, #image_type_9 OUTPUT, #image_type_9 OUTPUT, #image_desc_9 OUTPUT, #image_comments_9 OUTPUT
IF #row_counter = 10 EXEC sp_executesql #sql, #params, #image_type_10 OUTPUT, #image_type_10 OUTPUT, #image_desc_10 OUTPUT, #image_comments_10 OUTPUT
SET #row_counter = #row_counter + 1
END

Executing remote stored procedure within sp_executesql

I'm trying to get IDENT_CURRENT value on the linked server. I've created a stored procedure sp_current_identity on the remote server that has output parameter.
CREATE PROCEDURE [dbo].[sp_current_identity] ( #strTableName nvarchar(255), #intRowId int OUTPUT )
AS
BEGIN
select IDENT_CURRENT(#strTableName)
END
After that I have created two synonyms:sp_current_identity and sometable.
I need to execute sp_current_identity using sp_executesql (I'm creating a custom DataAtapter to work with synonyms via LLBLGEN 3.1). Please see the following example:
declare #p4 int
set #p4=NULL
exec sp_executesql N'SET XACT_ABORT ON; INSERT INTO [db].[dbo].[sometable] ([FieldName], [TableName], [UserField]) VALUES (#p1, #p3, #p4) ;
exec dbo.sp_current_identity #p5, #p2
;SET XACT_ABORT OFF',N'#p1 varchar(50),#p2 int output,#p3 varchar(50),#p4 varchar(50), #p5 varchar(200)',
#p1='test24',#p2=#p4 output,#p3='test24',#p4='test5',#p5='sometable'
select #p4
It works fine when this code is executed on the remote server (where sp_current_identity is local stored procedure), but it causes an exception when the code is executed on the local server.
Here is the error:
Procedure or function 'sp_current_identity' expects parameter '#strTableName', which was not supplied.
Thanks for your help!
Have you considered running EXEC remoteserver.database.dbo.sp_executesql 'dynamic SQL'; instead of trying to execute the dynamic SQL locally? The sp_current_identity procedure has to exist at the place where the query is actually executed, not where the query is called from.
I found that I had to assemble my dynamic call to the remote server in two steps. I was trying to get the Database ID:
DECLARE #sql nvarchar(4000)
DECLARE #parmDefinition nvarchar(500)
SET #parmDefinition = N'#retvalOUTside int OUTPUT'
SET #sql = 'SELECT TOP 1 #retvalOUT = database_id FROM [' + #ServerName + '].master.sys.databases WHERE name = ''''' + #dbname + ''''''
DECLARE #SPSQL nvarchar(4000) = '
DECLARE #DBID INT;
DECLARE #parmDefinition nvarchar(500);
SET #parmDefinition = N''#retvalOUT int OUTPUT'';
DECLARE #SQLinside nvarchar(400) =''' + #sql + ''';
EXEC [' + #ServerName + '].master.dbo' + '.sp_executeSQL #SQLinside, #parmDefinition, #retvalOUT = #retvalOUTside OUTPUT'
EXEC sp_executeSQL #SPSQL, #parmDefinition, #retvalOUTside=#DBID OUTPUT

Resources