Dynamic SQL with Dynamic Parameters - sql-server

I am trying to write dynamic SQL that can accept up to 50 parameters. It can be 1 or 10 or 15. The limit is 50 from the main sproc but the input to the dynamic sql is not defined.
I have created this dynamic statement through a while loop. It will read the number of parameter in a table(3 in this example) and generates the statement below. I am not concerned with the performance of the while loop because the tables/input are very small:
--generated from while loop
DECLARE #rParam1 NVARCHAR(MAX)
DECLARE #rParam2 NVARCHAR(MAX)
DECLARE #rParam3 NVARCHAR(MAX)
SELECT #rParam1= Parameter_Name FROM [Template_Params] WHERE Key=12345 AND Sort_Order=1
SELECT #rParam2= Parameter_Name FROM [Template_Params] WHERE Key=12345 AND Sort_Order=2
SELECT #rParam3= Parameter_Name FROM [Template_Params] WHERE Key=12345 AND Sort_Order=3
DECLARE #BODY NVARCHAR(MAX);
SELECT #BODY = Body FROM [Template] WHERE Key=12345
SET #Body = REPLACE(#Body,#rparam1,#PARAM_Input1)
SET #Body = REPLACE(#Body,#rparam2,#PARAM_Input2)
SET #Body = REPLACE(#Body,#rparam3,#PARAM_Input3)
SELECT #BODY
This so far works good.
The stored procedure can have 50 inputs but for example I will limit to 5 (only used 3)
--this is from the main sproc
DECLARE #PARAM_Input1 NVARCHAR(MAX)='test',
#PARAM_Input2 NVARCHAR(MAX)='test1',
#PARAM_Input3 NVARCHAR(MAX)='test2',
#PARAM_Input4 NVARCHAR(MAX),
#PARAM_Input5 NVARCHAR(MAX),
#param_input6 nvarchar(max)
Now I need to get these values into the dynamic SQL so my final Dynamic statement should have
SET #Body = REPLACE(#Body,#rparam1,'test')
SET #Body = REPLACE(#Body,#rparam2,'test1')
SET #Body = REPLACE(#Body,#rparam3,'test2')
This is where I am stuck, I do not want to write the REPLACE Statement 50 times like this.
SET #SQL=REPLACE(#SQL,'#PARAM_Input1',''''+#PARAM_Input1+'''')
I tried a cursor but the name of the variable has to be dynamic and it's not in the same scope so the values from the sproc are not being passed.

It might not be possible in SQL to do what you have in mind. A solution could be if you can send a table as parameter to contain the information as below (or generate it dinamically:
ParameterName | Value
<value of #rParam1> | <value of #Param_Input1>
<value of #rParam2> | <value of #PARAM_Input2>
Then, update #BODY variable
SELECT #BODY = Body FROM [Template] WHERE Key=12345
UPDATE <TableParameter_Or_Variable>
SET #Body = REPLACE(#Body, ParameterName, Value)
I don't know how you obtain the values for #PARAM_InputX, but for `#rParamX' you can do something like:
INSERT INTO #Table
SELECT Parameter_Name AS ParameterName, null AS Value
[Template_Params] WHERE Key=12345
ORDER BY sort_order
and then update the rows accordingly. On #Table you can open a cursor and match the current row to #PARAM_InputX.

Related

Return and Store string from an execute statement to be used later

I've got a procedure call that is used by several groups/processes etc.
The call works as follows:
EXEC LWP_PAYMENT_URL #order_no, #dept
and it returns a string like this
NzI2NzU4NabNzEyMj24Ny1zYQ=
I'm given the assignment to create a url path as follows
DECLARE #url_path VARCHAR(4000)
SET #url_path = 'https://www.website.com/payment?code='
DECLARE #ReturnValue VARCHAR(4000) = ''
EXEC #ReturnValue = LWP_PAYMENT_URL #order_no, #dept
SET #url_path = #url_path + #ReturnValue
SELECT #ReturnValue, #url_path
My goal is to take the hard coded url_path and get the encoded string from the execute and save it in a variable and concatenate it to the url_path.
What I'm seeing is that the string is returned part of the execute call instead of setting it to #ReturnValue and then looks like I get a zero value being saved and concatenated.
Added these are the final two lines of the LWP_PAYMENT_URL procedure.
DECLARE #Encoded VARCHAR(500) = CONVERT(VARCHAR(500), (SELECT CONVERT(VARBINARY, #string) FOR XML PATH(''), BINARY BASE64))
SELECT #Encoded AS [Encoded]
Thank you
Your stored procedure should be doing this instead:
CREATE OR ALTER PROCEDURE dbo.LWP_PAYMENT_URL
...#input parameters...,
#encoded varchar(500) = NULL OUTPUT
AS
BEGIN
SET NOCOUNT ON;
...
SET #Encoded = CONVERT(varchar(500),
(SELECT CONVERT(VARBINARY, #string) FOR XML PATH(''), BINARY BASE64));
END
And then the caller says:
DECLARE #ReturnValue varchar(500);
EXEC dbo.LWP_PAYMENT_URL #order_no, #dept,
#Encoded = #ReturnValue output;
If you can't change the stored procedure, create a separate one, or a table-valued UDF as suggested in the comments, or (assuming there are no other SELECTs in the procedure we can't see):
CREATE TABLE #foo(ReturnValue varchar(500));
INSERT #foo EXEC dbo.LWP_PAYMENT_URL ...;
DECLARE #ReturnValue varchar(500);
SELECT #ReturnValue = ReturnValue FROM #foo;
That's gross, though, and basically an abuse of how data sharing should work in SQL Server.
Ideally what you should do is, if the logic is the same for all uses, put that logic in some type of module that is much easier to reuse (e.g. a table-valued function). Then this existing stored procedure can maintain the current behavior (except it would call the function instead of performing the calculation locally), and you can create a different stored procedure (or just call the function directly, if this is all your code is doing), and the logic doesn't have to be duplicated, and you don't have to trample on their stored procedure.

Save dynamic result from stored procedure into a dynamic table

I am facing some issues in saving the execution of stored procedure / scalar function into a table variable.
The function / stored procedure returns dynamic columns and I need to create a dynamic table to save the result of that function into it so that I can use the table.
Example: the stored procedure spGetEmployeeInfo could return employee name, employee id, etc. on such criteria they return only employee name,.
Is there a way to create a dynamic table and save the result into it after execute the stored procedure, or any suggestion.
Thanks
I don't like to get into this situation too often, but when I do, what I do is have the stored proc output into a global temp table. The name of the table is passed in as a parameter by the user. For instance:
create procedure dynamicBeCareful
#toggle bit,
#globalTempTableName varchar(50)
as
-- initializations
if left(#globalTempTableName,2) <> '##'
throw 50000, '#globalTempTableName must start with ##', 1;
declare #sql varchar(max);
-- build dynamic sql
if #toggle = 1
set #sql = 'select * into #tempTable from table1';
else
set #sql = 'select * into #tempTable from table2';
set #sql = replace(#sql, '#tempTable', #globalTempTableName);
-- terminations
exec (#sql);
declare #msg = 'Your results are in ' + #globalTempTableName;
print (#msg);
Then use it like this:
exec dynamicBeCareful 1, '##temp';
select * from ##temp;
Beyond just being dynamic in output, it also can get you out of nested insert-exec limitations.

SQL Server Stored Procedure insert query with given parameters

first of all, sorry for my English,
I am a newbie in stored procedure, so i'm seek for a help on it.
I have a project that need me to create a SP for a configurable table name and column name. I've manage to pass the table name and column name value from vb/vb.net and now i'm stuck on SP, below are sample of my code.
example :
frmTblname = table_a
frmClmnName = clm_A1, clm_A2, clm_A3, clm_A4, clm_A5,
toTblName = table_b,
toClmnName = clmn_b1,clmn_b2, clmn_b3, clmn_b4, clmn_b5
from vb/vb.net Rslt = ConnectionExec.RunSP(con, "sp_configurable_insert", frmTblname, frmClmnName, toTblName, toClmnName)
how to add that into SQL insert query?
Here are my SP
CREATE procedure [dbo].[sp_configurable_insert] #fromTable nvarchar(50),#fromColumn nvarchar(4000),#toTable nvarchar(50),#toColumn nvarchar(4000)
I've tried this but it seems not giving any result.
set #Query1 = 'insert into '+#toTable+'('+quotename(#toColumn)+')
select top 20 '+#fromColumn+'
from '+#fromTable+'
can anyone help me, please?
Thanks :)
Things to change:
quotename(#toColumn) will return '[clmn_b1,clmn_b2, clmn_b3, clmn_b4, clmn_b5]' which is not correct. Remove quotename() or set #toColumn = '[clmn_b1], [clmn_b2], [clmn_b3], [clmn_b4], [clmn_b5]'.
your query statement is unfinished.
Stored procedure (with correct syntax):
CREATE procedure [dbo].[sp_configurable_insert]
#fromTable nvarchar(50),
#fromColumn nvarchar(4000),
#toTable nvarchar(50),
#toColumn nvarchar(4000)
AS
BEGIN
DECLARE
#stm nvarchar(max),
#err int
SET #stm =
N'insert into '+#toTable+' ('+#toColumn+') select top 20 '+#fromColumn+' from '+#fromTable
EXEC #err = sp_executesql #stm
IF #err <> 0 BEGIN
RETURN #err
END
RETURN 0
END
you need to add EXECUTE(#Query1) to your SP and it will work
Add at the end of your query:
exec sp_executesql #Query1
So it should look like:
set #Query1 = 'insert into '+#toTable+'('+quotename(#toColumn)+')
select top 20 '+#fromColumn+'
from '+#fromTable+'
exec sp_executesql #Query1
So pay attention that #Query1 is incomplete or contains error coz quotes is unbalanced.

Setting a variable to a certain value or default to all values in a column

Sorry if my heading is very clear. I am trying to create a stored procedure :
Create Procedure Pro1 #a nvarchar = null, #b nvarchar =null
as
Select * from table1
where Parameter1 = '1'
and Parameter2 = #a
go
Above is not the actual code but i hope it explains what i am trying to achieve. I am selecting on two parameters. 'Parameter2' depends on the variable #a. The challenge i have is that if #a remains null at execution; i want the select statement to search on all values of Parmeter2 as if the 'and' statement was not there. I could write a if else statement to execute different selects but i was hoping i could simply set #a to some sort
of wildcard value that would simply do this? Maybe something : 'like []'?
CREATE PROCEDURE dbo.Pro1 -- use schema prefix when creating objects!
#a NVARCHAR(32) = NULL -- 32 should match data type of Parameter2 column
AS
BEGIN -- wrap your body and indent for readability
SET NOCOUNT ON; -- should always turn off DONE_IN_PROC messages
SELECT columns -- please name your columns instead of SELECT *
FROM dbo.table1 -- use schema prefix when referencing objects
WHERE (Parameter2 = #a OR #a IS NULL); -- semi-colons please!
END
GO
In some cases you may find that if you have a lot of these optional parameters, SQL Server will compile a plan for one set of parameters and that plan will not be very beneficial for different parameters. So you may want something like this:
CREATE PROCEDURE dbo.Pro1
#a NVARCHAR(32) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'SELECT columns FROM dbo.table1 WHERE 1=1';
IF #a IS NOT NULL
SET #sql += ' AND Parameter2 = #a';
EXEC sp_executesql #sql,
N'#a NVARCHAR(32)',
#a;
END
GO

Parameterizing XPath for modify() in SQL Server XML Processing

Just like the title suggests, I'm trying to parameterize the XPath for a modify() method for an XML data column in SQL Server, but running into some problems.
So far I have:
DECLARE #newVal varchar(50)
DECLARE #xmlQuery varchar(50)
SELECT #newVal = 'features'
SELECT #xmlQuery = 'settings/resources/type/text()'
UPDATE [dbo].[Users]
SET [SettingsXml].modify('
replace value of (sql:variable("#xmlQuery"))[1]
with sql:variable("#newVal")')
WHERE UserId = 1
with the following XML Structure:
<settings>
...
<resources>
<type> ... </type>
...
</resources>
...
</settings>
which is then generating this error:
XQuery [dbo.Users.NewSettingsXml.modify()]: The target of 'replace' must be at most one node, found 'xs:string ?'
Now I realize that the modify method must not be capable of accepting a string as a path, but is there a way to accomplish this short of using dynamic SQL?
Oh, by the way, I'm using SQL Server 2008 Standard 64-bit, but any queries I write need to be compatible back to 2005 Standard.
Thanks!
In case anyone was interested, I came up with a pretty decent solution myself using a dynamic query:
DECLARE #newVal nvarchar(max)
DECLARE #xmlQuery nvarchar(max)
DECLARE #id int
SET #newVal = 'foo'
SET #xmlQuery = '/root/node/leaf/text()'
SET #id = 1
DECLARE #query nvarchar(max)
SET #query = '
UPDATE [Table]
SET [XmlColumn].modify(''
replace value of (' + #xmlQuery + '))[1]
with sql:variable("#newVal")'')
WHERE Id = #id'
EXEC sp_executesql #query,
N'#newVal nvarchar(max) #id int',
#newVal, #id
Using this, the only unsafe part of the dynamic query is the xPath, which, in my case, is controlled entirely by my code and so shouldn't be exploitable.
The best I could figure out was this:
declare #Q1 varchar(50)
declare #Q2 varchar(50)
declare #Q3 varchar(50)
set #Q1 = 'settings'
set #Q2 = 'resources'
set #Q3 = 'type'
UPDATE [dbo].[Users]
SET [SettingsXml].modify('
replace value of (for $n1 in /*,
$n2 in $n1/*,
$n3 in $n2/*
where $n1[local-name(.) = sql:variable("#Q1")] and
$n2[local-name(.) = sql:variable("#Q2")] and
$n3[local-name(.) = sql:variable("#Q3")]
return $n3/text())[1]
with sql:variable("#newVal")')
WHERE UserId = 1
Node names are parameters but the level/number of nodes is sadly not.
Here is the solution we found for parameterizing both the property name to be replaced and the new value. It needs a specific xpath, and the parameter name can be an sql variable or table column.
SET Bundle.modify
(
'replace value of(//config-entry-metadata/parameter-name[text() = sql:column("BTC.Name")]/../..//value/text())[1] with sql:column("BTC.Value") '
)
This is the hard coded x path: //config-entry-metadata/parameter-name ... /../..//value/text()
The name of the parameter is dynamic: [text() = sql:column("BTC.Name")]
The new value is also dynamic: with sql:column("BTC.Value")

Resources