Is it possible to supply sp_ExecuteSql parameter names dynamically? - sql

Is it possible to supply the list of parameters to sp_ExecuteSql dynamically?
In sp_ExecuteSql the query and the parameter definitions are strings. We can use string variables for these and pass in any query and parameter definitions we want to execute. However, when assigning values to the parameters, we cannot seem to use strings or string variables for the parameter names.
For example:
DECLARE #SelectedUserName NVARCHAR(255) ,
#SelectedJobTitle NVARCHAR(255);
SET #SelectedUserName = N'TEST%';
SET #SelectedJobTitle = N'%Developer%';
DECLARE #sql NVARCHAR(MAX) ,
#paramdefs NVARCHAR(1000);
SET #sql = N'select * from Users where Name LIKE #UserName '
+ N'and JobTitle LIKE #JobTitle;'
SET #paramdefs = N'#UserName nvarchar(255), #JobTitle nvarchar(255)';
EXEC sp_ExecuteSql #sql, #paramdefs, #UserName = #SelectedUserName,
#JobTitle = #SelectedJobTitle;
The query #sql, and the parameter definitions, #paramdefs, can be passed into sp_ExecuteSql dynamically, as string variables. However, it seems to me that when assigning values to the parameters we cannot assign dynamically and must always know the number of parameters and their names ahead of time. Note in my example how I could declare parameters #UserName and #JobTitle dynamically and pass in that declaration as a string variable, but I had to explicitly specify the parameter names when I wanted to set them. Is there any way around this limitation?
I would like to be able to both declare the parameters dynamically and assign to them dynamically as well. Something like:
EXEC sp_ExecuteSql #sql, #paramdefs,
N'#UserName = #SelectedUserName, #JobTitle = #SelectedJobTitle';
Note that this doesn't actually work but illustrates the sort of thing I'd like to happen. If this sort of thing worked then I could pass in different queries with different numbers of parameters which have different names. The whole thing would be dynamic and I wouldn't have to know the names or numbers of parameters beforehand.

You can do this by using a table valued parameter as the only parameter:
DECLARE #YourQuery NVARCHAR(MAX0 = '<your dynamic query>'
CREATE TYPE dbo.SqlVariantTable AS TABLE
(
[Name] VARCHAR(255),
Type VARCHAR(255),
Value SQL_VARIANT
)
DECLARE #Table SqlVariantTable;
-- Insert your dynamic parameters here:
INSERT INTO #Table
VALUES
('Parameter1', 'VARCHAR(255)', 'some value'),
('Parameter2', 'INT', 3),
DECLARE #ParameterAssignment NVARCHAR(MAX)
SELECT #ParameterAssignment = ISNULL(#ParameterAssignment + ';','') + 'DECLARE ' + Name + ' ' + Type + ' = (SELECT CAST(Value AS ' + Type + ') FROM #p1 WHERE Name = ''' + Name + ''')'
FROM #Table
SET #YourQuery = #ParameterAssignment + ';' + #YourQuery
EXEC SP_EXECUTESQL #YourQuery, N'#p1 SqlVariantTable READONLY', #Table
Now you can simpy insert the parameters into the #Table variable, and they will be present with they original name and type within the query exeuted in the SP_EXECUTESQL. Only make sure you do not use VARCHAR(MAX) or NVARCHAR(MAX) variable types, since they are not supported by SQL_VARIANT. Use (for instance) VARCHAR(4000) instead

I also thought about this and couldn't find anything better than this:
BEGIN
DECLARE
#p1 int, #p2 int, #p3 int, #p4 int...;
DECLARE
#DynamicSQL NVARCHAR(MAX);
SET
#p1 = {some logic},
#p2 = {some different logic},
#p3 = {another logic},
#p4 = {yet another logic},
...;
SET
#DynamicSQL =
N'
some statement
doing
somethin
WHERE
someColumn = #p1
AND someAnotherColumn = #p2
/*no more parameters used below this line*/
';
exec sp_executesql
#stmt = #DynamicSQL,
#params = '#p1 int, #p2 int, #p3 int, #p4 int...'
#p1 = #p1, #p2 = #p2, #p3 = #p3, #p4 = #p4, ...
END;
Notice, that #DynamicSQL uses only 2 out of the 4 possible parameters. Parameters #p1 int, #p2 int, #p3 int, #p4 int... represent the maximum number of parameters you can use in your #DynamicSQL.
You have to have a predefined maximum number of parameters that can be used, and you build the #DynamicSQL statement only with some subset of them. Parameters defined in #params that are not present in the #stmt statement are ignored.
It is not 100 % universal, but I guess that using more than 200 dynamic parameters smells of code smell.

You're trying to work one level too high in abstraction.
Arbitrary parameters requires dynamic SQL, a.k.a. building SQL via strings, which then makes the entire point of parameters moot.
Instead, this should be handled as parameters in the calling code, such as C#, which will allow you to take any SQL statement in a string, apply an arbitrary number of arguments, and execute it.

While this doesn't answer my question I thought it may be useful for others in similar situations. I've discovered the following:
If you have a fixed number of parameters but don't know their names you can pass the parameter values by position rather than name. The following will work:
exec sp_ExecuteSql
#sql,
#paramdefs,
#SelectedUserName, #SelectedJobTitle;
or
exec sp_ExecuteSql
#sql,
#paramdefs,
N'TEST%', N'%Developer%';

Please try this.
Declare #UName varchar(50)
Declare #Job varchar(50)
Set #UName = 'TEST%'
Set #Job = '%Developer%'
exec sp_ExecuteSql #sql, #paramdefs, #UserName = #UName, #JobTitle = #Job;
May this will help you.
Ref From technet.Microsoft.com
Ex.
DECLARE #IntVariable int;
DECLARE #SQLString nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
/* Build the SQL string one time.*/
SET #SQLString = N'SELECT BusinessEntityID, NationalIDNumber, JobTitle, LoginID
FROM AdventureWorks2012.HumanResources.Employee
WHERE BusinessEntityID = #BusinessEntityID';
SET #ParmDefinition = N'#BusinessEntityID tinyint';
/* Execute the string with the first parameter value. */
SET #IntVariable = 197;
EXECUTE sp_executesql #SQLString, #ParmDefinition,
#BusinessEntityID = #IntVariable;
/* Execute the same string with the second parameter value. */
SET #IntVariable = 109;
EXECUTE sp_executesql #SQLString, #ParmDefinition,
#BusinessEntityID = #IntVariable;
For dynamic you have to pass something like this
EXECUTE sp_executesql N'Select * from Admin WHERE ID = #ID and FirstName=#FName',
N'#ID tinyint, #FName varchar(250)',
#ID = 2, #FName = 'admin';

Related

Removing NULL with COALESCE in Dynamic SQL

I have some dynamic SQL as part of a stored procedure I want to execute:
SET #SQL_TXT = 'INSERT INTO ' +#ENTY_TABLE_NAME+
'([ITEM_NAME]
,[ADD_DTT]
,[ADD_USR]
,[UPD_DTT]
,[UPD_USR]
,[ACTIVE_IND]
,[ITEM_PK])
VALUES
('''+#UPD_VALUE+'''
, CURRENT_TIMESTAMP
, '''+#UPD_USR_DOM_NAME+''', CURRENT_TIMESTAMP,'''+#UPD_USR_DOM_NAME+''',''Y'','''+#ITEM_PK+''');
SET #Id = SCOPE_IDENTITY();
RETURN;'
This runs fine, but ITEM_NAME can't be NULL so I want to ad a COALESE():
SET #SQL_TXT = 'INSERT INTO ' +#ENTY_TABLE_NAME+
'(COALESCE([ITEM_NAME], '')
,[ADD_DTT]
,[ADD_USR]
,[UPD_DTT]
,[UPD_USR]
,[ACTIVE_IND]
,[ITEM_PK])
VALUES
('''+#UPD_VALUE+'''
, CURRENT_TIMESTAMP
, '''+#UPD_USR_DOM_NAME+''', CURRENT_TIMESTAMP,'''+#UPD_USR_DOM_NAME+''',''Y'','''+#ITEM_PK+''');
SET #Id = SCOPE_IDENTITY();
RETURN;'
But I am getting this error:
Incorrect syntax near the keyword 'COALESCE'.
Unclosed quotation mark after the character string ');
SET #Id = SCOPE_IDENTITY();
RETURN;'.
Incorrect syntax near '='.
For the life of me I don't see where this ')' is. What am I doing wrong?
Edit: here is the exec
EXECUTE SP_executesql #SQL_TXT, N'#Id INTEGER OUTPUT', #Id OUTPUT
You can not put coalesce() around the column name destination of your insert, you use it around the value being inserted.
SET #SQL_TXT = 'INSERT INTO ' +#ENTY_TABLE_NAME+
'([ITEM_NAME]
,[ADD_DTT]
,[ADD_USR]
,[UPD_DTT]
,[UPD_USR]
,[ACTIVE_IND]
,[ITEM_PK])
VALUES
('''+coalesce(#UPD_VALUE,'')+'''
, CURRENT_TIMESTAMP
, '''+#UPD_USR_DOM_NAME+''', CURRENT_TIMESTAMP,'''+#UPD_USR_DOM_NAME+''',''Y'','''+#ITEM_PK+''');
SET #Id = SCOPE_IDENTITY();
RETURN;'
Note: #UPD_USR_DOM_NAME is inserted into two different columns. Not sure if that is intentional, just thought I would point it out.
You can also fully parameterize the rest of your values for use with sp_executesql instead of concatenating them like that. (Guessing at the data types of your parameters in this example)
declare #sql nvarchar(max);
declare #params nvarchar(max);
declare #id int;
set #sql = N'INSERT INTO ' +#ENTY_TABLE_NAME+'([ITEM_NAME] ,[ADD_DTT] ,[ADD_USR] ,[UPD_DTT] ,[UPD_USR] ,[ACTIVE_IND] ,[ITEM_PK])
VALUES (coalesce(#UPD_VALUE,''), CURRENT_TIMESTAMP, #UPD_USR_DOM_NAME, CURRENT_TIMESTAMP,#UPD_USR_DOM_NAME,''Y'',#ITEM_PK);
SET #Id = SCOPE_IDENTITY();
RETURN;'
set #params = N'#UPD_VALUE varchar(32),#UPD_USR_DOM_NAME varchar(32), #ITEM_PK varchar(32), #Id INTEGER OUTPUT';
EXECUTE SP_executesql #sql, #params, #UPD_Value, #UPD_USER_DOM_NAME, #ITEM_PK, #Id = #Id OUTPUT;
dynamic sql reference:
The curse and blessings of dynamic SQL - Erland Sommarskog
sp_executesql

Dynamic SQL with parameters

I have a SQL query stored in a table that contains parameter names. I need to know how to execute it properly in a stored procedure.
This is my SQL code in the procedure
PROCEDURE [spMassUpdateSKUs]
#SKU AS NVARCHAR(20)
,#FIELD AS NVARCHAR(50)
,#VALUE AS NVARCHAR(50)
,#RESULT as Int = Null Output
AS
BEGIN
IF EXISTS(SELECT CODENUMBER FROM INVENTORY_MASTER WHERE CODENUMBER=#SKU)
BEGIN
DECLARE #SQLQUERY AS NVARCHAR(50)
SET #SQLQUERY=(SELECT SQLUPDATE FROM MASS_UPDATE WHERE DROPDOWNLABEL=#FIELD)
EXEC SP_EXECUTESQL #SQLQUERY
END
and this is the sql query from the table
update inventory_master_flex set departmentid=#value where codenumber=#sku
I've tried replacing with the real parameters but that doen't work.
SELECT REPLACE(REPLACE(#SQLQUERY,'#VALUE',#VALUE),'#SKU',#SKU)
-- 50 is too short for sure; you may try 1000 or different number
DECLARE #SQLQUERY AS NVARCHAR(MAX)
-- for debug purpose
PRINT #SQLQUERY
-- params
EXEC SP_EXECUTESQL #SQLQUERY, N'#Value NVARCHAR(50), #sku NVARCHAR(50)`, #Value, #sku
REPLACE is not good in case of strings with quotes and so on which would brake the #sqlquery code.
Pass the parameters in using sp_executesql, not replace():
IF EXISTS(SELECT CODENUMBER FROM INVENTORY_MASTER WHERE CODENUMBER=#SKU)
BEGIN
DECLARE #SQLQUERY AS NVARCHAR(MAX);
SET #SQLQUERY = (SELECT SQLUPDATE FROM MASS_UPDATE WHERE DROPDOWNLABEL = #FIELD);
EXEC SP_EXECUTESQL #SQLQUERY, N'#SKU VARCHAR(255), #VALUE VARCHAR(255)', #SKU = #SKU, #VALUE = #VALUE
END;
I don't know what the types are. But if one or both are strings or dates, then you would need single quotes in your implementation. However, you are already using sp_executesql so go whole-hog and pass in parameters as well.

SQL Store a value from an EXECUTE Command into a variable

I'm attempting to store a value into a variable from a EXECUTE command. I know I'm suppose to use sp_executesql command, but all examples online are only making more confused. So here is what I'm trying to do.
I have a stored procedure that accepts two parameters (a table name, a room #). To have a dynamic table name, I use dynamic SQL style while using strings. I'm attempting to store a phone number that is either from multiple tables. I got this working so far.
DECLARE #Location varchar(MAX);
DECLARE #Room varchar(10);
DECLARE #Number char(8);
DECLARE #SQLString varchar(MAX);
SET #Location = N'CMPhone.dbo.GardenCottage';
SET #Room = N'202';
SET #SQLString ='SET #Number = (SELECT PhoneNumber FROM ' + #Location + ' WHERE Room = ''' + #Room + ''');';
PRINT(#SQLString);
OUTPUT
SET #Number = (SELECT PhoneNumber FROM CMPhone.dbo.GardenCottage WHERE Room = '202');
SET #Number = (SELECT PhoneNumber FROM CMPhone.dbo.GardenCottage WHERE Room = '202');
PRINT(#Number);
OUTPUT
123-4567
Which is the correct number. Now, here is where the problem comes in. I need to do another query using dynamic SQL so I can use multiple tables again. So in my stored procedure, I need to store my EXEC(#SQLString) into a variable (#Number) so I can use that value, and that's where I'm having problems. I can't get sp_executesql to store the value into #Number. The other query will look something like this
SET #SQLString = ' UPDATE PhoneNumbers SET Active = ''1'' WHERE
PhoneNumber = ''' + #Number + ''';';
EXEC(#SQLString);
If this is confusing in anyway, or you have questions, please ask. Any help is very much appreciated. Thanks
Update #1:
I have this new string now
#SQLString = 'SELECT PhoneNumber FROM ' + #Location ' + ' WHERE Room = ''' + #Room + ''';';
EXECUTE SP_EXECUTESQL #SQLString
gets the correct number, but I don't know how to set up a OUTPUT parameter.
I'm attempting to follow this example from Microsoft
DECLARE #SQLString NVARCHAR(500)
DECLARE #ParmDefinition NVARCHAR(500)
DECLARE #IntVariable INT
DECLARE #Lastlname varchar(30)
SET #SQLString = N'SELECT #LastlnameOUT = max(lname)
FROM pubs.dbo.employee WHERE job_lvl = #level'
SET #ParmDefinition = N'#level tinyint,
#LastlnameOUT varchar(30) OUTPUT'
SET #IntVariable = 35
EXECUTE sp_executesql
#SQLString,
#ParmDefinition,
#level = #IntVariable,
#LastlnameOUT=#Lastlname OUTPUT
SELECT #Lastlname
But I don't see how their declaring the lastlNameOUT variables.
use output variable in your EXECUTE sp_executesql like this:
EXECUTE sp_executesql #SQLString, N'#Number char(8) out',#Number out then you will get #Number value from inside dynamc sql, then you can use that value in other part of the query. hope this helps

Execute Stored Procedure from Stored Procedure w/ dynamic SQL capturing output

Inside a stored procedure (A) I need to call a stored procedure (X) inside a specific database and capture the output. X returns a single value.
From what I understand I need to provide the DB name of X to the stored procedure in A and I need to use dynamic SQL to build the query on execution targeting the desired database.
What am unable to figure out is how to capture output from X in A to work with the result.
You could use sp_executesql to dynamically call your nested Stored Procedure.
DECLARE #db AS SYSNAME
DECLARE #return_value AS INT
DECLARE #output_value AS INT
DECLARE #sql AS NVARCHAR(MAX)
-- Set your DB name
SET #db = N'mydb'
/*
Use sp_executesql to dynamically pass in the db and stored procedure
to execute while also defining the values and assigning to local variables.
*/
SET #sql = N'EXEC #rtn = ' + #db + '.dbo.[your_stored_procedure] #output OUTPUT'
EXEC sp_executesql #sql
, N'#rtn AS INT, #output AS INT OUTPUT'
, #return_value = #rtn
, #output_value = #output OUTPUT
Adding to the above answer, following is the way to call stored procedures dynamically by passing parameters.
DECLARE #SpName VARCHAR(1000)
SELECT #SpName = DeleteLiveSP FROM dbo.ArchivalInfo (NOLOCK) WHERE TableName = #TableName
DECLARE #SqlString nvarchar(2000)
DECLARE #ParamDef nvarchar(2000)
SET #SqlString = N'exec '+#SpName + ' #CriteriaParam'
SET #ParamDef = N'#CriteriaParam XML'
EXECUTE sp_executesql #SqlString ,#ParamDef, #CriteriaParam = #Criteria

Using variable inside dynamic SQL

I have the below SQL..What I am trying to do is use the Parameter defined at the stored procedure level inside dynamic SQL:
CREATE PROCEDURE [dbo].[Test]
(#DealID NVARCHAR(500),
#OUTPUT NVARCHAR(MAX) OUTPUT,
#FeeType CHAR(1)
) -- I want to use this parameter inside dynamic SQL query
AS
DECLARE #exec_str NVARCHAR(MAX)
DECLARE #ParmDefinition NVARCHAR(MAX)
BEGIN
SET #exec_str = N'DECLARE #ParmDefinition NVARCHAR(500)
SELECT * FROM #FeeType' --This is where I want to use the variable
DECLARE #ParamDefinition nvarchar(max)
SET #ParamDefinition = N'#OUTPUT NVARCHAR(MAX) OUTPUT'
EXEC sp_executesql #exec_str, #ParamDefinition
Can someone please tell me how to do it?
Thanks
In SQL Server Identifiers can't be parameterized.
Since you are using dynamic SQL anyway, you can do something like this:
SET #exec_str= N'Select * from '+ #FeeType
EXEC(#exec_str)
However, this is vulnerable to SQL injection attacks. To reduce the risk to minimum you should check first that such a table name exists, and I would also use quotename just to be on the safe side:
IF EXISTS
(
SELECT 1
FROM Information_Schema.Tables
WHERE TABLE_NAME = #FeeType
)
BEGIN
SET #exec_str= N'Select * from '+ QUOTENAME(#FeeType)
EXEC(#exec_str)
END