Dynamically Create Update SQL In Stored Procedure - sql

I try to create a stored procedure to update a table record whose sql statement is dynamically created. I wrote some codes but am stoped in to run this query dynamically, How can i run this query or is there a better solution for this problem.
How this SP work?=> I send the columns names,values and datatype of the record that need update to SP like below
<e columnName=''PaymentStatus'' value=''99'' type=''nvarchar''/>
<e columnName=''HotelProvider'' value=''GAT2'' type=''nvarchar''/>
Then travel the nodes and create an Update statement, but can't execute it :))
I am giving a part of SP to understand the question better.
DECLARE #UpdateXml xml = '
<xml>
<e columnName=''PaymentStatus'' value=''99'' type=''nvarchar''/>
<e columnName=''HotelProvider'' value=''GAT2'' type=''nvarchar''/>
</xml>';
DROP TABLE ##UpdateFields
SELECT
t.c.value('#columnName', 'varchar(max)') AS ColumnName,
t.c.value('#value', 'varchar(max)') AS Value,
t.c.value('#property', 'varchar(max)') AS PropertyOf,
t.c.value('#type', 'varchar(max)') AS ColumnType
INTO ##UpdateFields
from #UpdateXml.nodes('/xml/e') as t(c)
DECLARE #SQL nvarchar(MAX) = 'UPDATE HotelBooking ';
DECLARE #SQLUpdatePart nvarchar(MAX);
SET #SQLUpdatePart = 'SET ';
SELECT #SQLUpdatePart= #SQLUpdatePart+ColumnName +'='+'#QP_'+ColumnName+',' FROM ##UpdateFields WHERE PropertyOf IS NULL;
DECLARE #SQLWherePart nvarchar(MAX);
SET #SQLWherePart = ' WHERE Id=2';
DECLARE #ParmDefinition nvarchar(MAX)='';
SELECT #ParmDefinition = #ParmDefinition+'#QP_'+ColumnName+' '+ColumnType+',' FROM ##UpdateFields;
SELECT #ParmDefinition
SELECT #SQL + #SQLUpdatePart + #SQLWHerePart;
Last two select statements results are:
#QP_PaymentStatus nvarchar,#QP_HotelProvider nvarchar,#QP_TransactionId uniqueidentifier,#QP_UpdatedDate datetime
and
UPDATE HotelBooking SET PaymentStatus=#QP_PaymentStatus,HotelProvider=#QP_HotelProvider,UpdatedDate=#QP_UpdatedDate,TransactionId=#QP_TransactionId WHERE Id=2
Now How can I give the #QP parameters to sp_executesql() method dynamically?

You can do it by wrapping sp_executesql call in another exec:
declare #updateStr nvarchar(1000)
-- #updateStr = N'select * from ATable where ID = #p1'
set #updateStr = N'N''select * from ATable where ID = #p1'''
declare #paramStr nvarchar(100)
-- #paramStr = N'#p1 int'
set #paramStr = N'N''#p1 int'''
declare #actualParameters nvarchar(100)
set #actualParameters = N'#p1 = 10'
-- Concatenate parts of query into a variable
declare #sql nvarchar(max)
set #sql = N'sp_executesql ' + #updateStr + ',' + #paramStr + ', ' + #actualParameters
-- And voila!
exec (#sql)

Related

Passing a variable out of a SQL query

Is it possible to pass a variable out of a SQL query?
I am building up the initial query using a variable. I have added a simplified subset of my query below.
Thanks
declare #query Nvarchar(max)
declare #ColumnName Nvarchar(max)
set #ColumnName = 'MyColumn'
SET #query = 'Select ' + #ColumnName + ' from [MyTable] WHERE [MyCondition]'
EXECUTE sp_executesql #query
Can I return this result as a variable to pass to another query?
Yes. You use an output parameter:
declare #query Nvarchar(max);
declare #ColumnName Nvarchar(max);
declare #outval <type>; -- whatever type
set #ColumnName = 'MyColumn'
set #query = 'Select #outval =' + #ColumnName + ' from [MyTable] where [MyCondition]';
execut sp_executesql #query,
N'#outval <type> output',
#outval = #outval output;
Store the results in table variable and then convert it into XML.
Declare #xml XML
declare #query Nvarchar(max)
declare #ColumnName Nvarchar(max)
set #ColumnName = 'MyColumn'
declare #Table as TABLE(
MyColumn varchar(Max)-- Your Column datatype
)
SET #query = 'Select ' + #ColumnName + ' from [MyTable] WHERE [MyCondition]'
INSERT INTO #Table
EXECUTE sp_executesql #query
select #xml=MyColumn from #Table for XML PATH('')
How you want to pass returned result to other query?
What i can think of create a function return a table and call that function on other query:
CREATE FUNCTION test (#id int)
RETURNS #testTable TABLE(id int)
AS
begin
insert into #testTable select id from #your_table where id = #id
return
end
This will return a table you can check using :
select * from test(2); --will give you a table
If you want to use in a query:
`select * from #second_table where id in (select * from test2(#id parameter))` --- will filter query by id returned by function.

T-SQL Setting a scalar variable with the value of another scalar variable

Im creating a stored procedure that retrieves data to fill a radar chart. It worked pretty well using static tables und rows like this:
(This is just a piece of the code)
SELECT
#aAvg = CAST(AVG(1. * foerderpy_1617) as DECIMAL(18,4)),
#aMin = CAST(MIN(1. * foerderpy_1617) as DECIMAL(18,4)),
#aMax = CAST(MAX(1. * foerderpy_1617) as DECIMAL(18,4))
FROM foerderpy a WHERE SUBSTRING(a.BSN,3,1) = 'g';
But now i want a dynamic sql. I want the stored procedure to always take the latest row of my table:
(These are just pieces of the code)
DECLARE #SQL AS NVARCHAR(MAX);
DECLARE #aAvg AS NVARCHAR(MAX);
DECLARE #aMin AS NVARCHAR(MAX);
DECLARE #aMax AS NVARCHAR(MAX);
DECLARE #tabname SYSNAME;
DECLARE #coluname SYSNAME;
DECLARE #counter INTEGER;
SET #tabname = 'foerderpy'
SET #counter = (
SELECT MAX(ORDINAL_POSITION)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tabname
GROUP BY TABLE_NAME)
SET #coluname = (
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tabname AND
ORDINAL_POSITION = #counter)
SET #aAvg = (SELECT CAST(AVG(1. * #coluname) as DECIMAL(18,4))FROM #tabname a WHERE SUBSTRING(a.BSN,3,1) = SUBSTRING(#restriction,3,1))
At the last line (the SET #aAvg), the stored procedure stops working and sql tells me "i have to declare #tabname", although i obv. declared it above. What is the problem im missing? Is it even possible to do what im trying?
The rest of the Code isn't causing any problems so i left it out. I need the #aAvg to calculate later in the procedure.
You need to run your last query using EXECUTE because EXECUTE:
Executes a command string or character string within a Transact-SQL batch
So you have to change the last line of your procedure in a way that the query is written in a string and called by execute.
DECLARE #sql VARCHAR(max) = 'SELECT CAST(AVG(1. * ' + #coluname + ') as DECIMAL(18,4))FROM '+ #tabname +' a WHERE SUBSTRING(a.BSN,3,1) = SUBSTRING('+#restriction+',3,1)';
EXECUTE(#sql);
If you would like to save the value in your variable #aAvg, you can use sp_executesql with an out parameter, this way:
DECLARE #sql VARCHAR(max) = 'SELECT CAST(AVG(1. * ' + #coluname + ') as DECIMAL(18,4))FROM '+ #tabname +' a WHERE SUBSTRING(a.BSN,3,1) = SUBSTRING('+#restriction+',3,1)';
exec sp_executesql #sql, N'#aAvg decimal(18,4) out', #aAvg out
select #aAvg

T sql - How to store results from a dynamic query using EXEC or EXECUTE sp_executesql

I am trying to write a dynamic query. Let's say i have a table like below, which represents the hierarchy level of a sales agent:
AgentNumber Level1Agent Level2Agent Level3Agent Level4Agent Level5Agent
1122334455 1122334499 1122334488 1122334477 1122334466 1122334455
I want to be able to dynamically select a level based on a specified agent. My EXECUTE statement seems to work correctly, but how do I get the result stored in a variable I can use later? Every answer I have found seems to only get me a success return variable, not the actual query result.
Below is my code:
DECLARE #level INT = 1;
DECLARE #agent CHAR(10) = 1122334455;
DECLARE #colname NVARCHAR(11) = CONCAT('Level',#level,'Agent');
DECLARE #whereclause NVARCHAR(35) = CONCAT('WHERE AgentNumber = ',#agent);
DECLARE #qry NVARCHAR(300) = 'SELECT ' + #colname + ' FROM dbo.TABLE ' + #whereclause;
DECLARE #up NVARCHAR(10);
EXECUTE sp_executesql #qry, #up OUT
SELECT #up
The output of #up is NULL. If I change the last two lines to:
EXECUTE #up = sp_executesql #qry
SELECT #up
Now the output of #up is 0.
I want the output of 1122334499 and I need it stored in a variable that can later be used and inserted into a table.
Here is a fully functional example of how you can do this. Notice this is using a parameterized where clause and quotename around the column name in the dynamic sql to prevent sql injection.
if OBJECT_ID('tempdb..#Agents') is not null
drop table #Agents
create table #Agents
(
AgentNumber char(10)
, Level1Agent char(10)
, Level2Agent char(10)
, Level3Agent char(10)
, Level4Agent char(10)
, Level5Agent char(10)
)
insert #Agents
select '1122334455', '1122334499', '1122334488', '1122334477', '1122334466', '1122334455'
DECLARE #level INT = 3;
DECLARE #agent CHAR(10) = 1122334455;
DECLARE #colname NVARCHAR(11) = CONCAT('Level',#level,'Agent');
declare #agentout char(10)
DECLARE #qry NVARCHAR(300) = 'SELECT #agent_out = ' + quotename(#colname) + ' FROM #Agents WHERE AgentNumber = #agentin';
EXECUTE sp_executesql #qry, N'#agentin char(10), #agent_out char(10) output', #agentin = #agent, #agent_out = #agentout output
select #agentout
You can try this :
DECLARE #level INT = 1;
DECLARE #agent CHAR(10) = 1122334455;
DECLARE #colname NVARCHAR(11) = CONCAT('Level',#level,'Agent');
DECLARE #whereclause NVARCHAR(35) = CONCAT('WHERE AgentNumber = ',#agent);
DECLARE #qry NVARCHAR(300) = 'SELECT #agentout=' + #colname + ' FROM dbo.TABLE ' + #whereclause;
DECLARE #up NVARCHAR(10);
EXECUTE sp_executesql #qry, N'#agentout NVARCHAR(10) OUTPUT', #agentout=#up OUTPUT
SELECT #up
Create a variable table and makes your query insert the results you want there. Something like this:
declare #results table(field1 varchar(max), field2 varchar(max));
declare #sqlStatement varchar(max);
set #sqlStatement = 'insert into #results(field1, field2) select field1, field2 from table';
EXECUTE #sqlStatement;
select * from #results; --It will print the results from your sql statement!

Get column value from string column name sql

Is this possible to get multiple columns value when we have column name as string
Like if i have a table Test and i have columns FirstName , LastName , Address .
Now what i want to get value of all three columns but i want to make this dynamic so that i just pass string column name i get values for that columns
Example
Select
(select column_name from metadata )
from source table
Pass the column names as parameters
DECLARE #COLS NVARCHAR(MAX)
DECLARE #TABLE NVARCHAR(MAX)
SET #COLS = 'COL1,COL2'
SET #TABLE = 'TABLENAME'
Now execute the query
DECLARE #QRY NVARCHAR(MAX)
SET #QRY = 'SELECT (SELECT '+#COLS+' FROM '+#TABLE+') FROM sourcetable'
EXEC SP_EXECUTESQL #QRY
You can build the query in code dynamically. However it needs to be robust so that it does not gets prone to SQL injection. Something like this:
string commandString = "select {0} from SomeTable";
SqlCommand command = new SqlCommand();
command.CommandText = string.Format(commandString, "selected column names");
command.EndExecuteReader();
In SQL:
declare #query nvarchar(500)
set #query = replace('select 0 from author','0','column names from some parameter')
execute sp_executesql #query
Update 2: Does this do what you need?
declare #query nvarchar(500)
DECLARE #columnNames varchar(1000)
set #columnNames = ''
SELECT #columnNames = #columnNames + column_name + ',' FROM metadata
set #query = replace('select 0 from source_table','0',SUBSTRING(#columnNames,0,LEN(#columnNames)-1))
execute sp_executesql #query

Getting output in a variable from dynamic SQL

I am using a dynamic sql i.e.
DECLARE #searchstring VARCHAR(500)
DECLARE #str VARCHAR(MAX)
SELECT #str = 'SELECT * FROM Table1 WHERE ' + #searchstring
EXECUTE #str
What I need is I want to select one column value from above dynamic sql to pass in a different SP
Let's say I need ID column value and pass it to another sp named GetAnotherData #Ids. How can I do that?
well you can go with Alexander Fedorenko example, but if you don't want to create any temp tables, you can use output xml parameter to pass your ids:
declare #stmt nvarchar(max), #Data xml, #searchstring nvarchar(max)
select #stmt = '
select #Data = (
select id
from Table1
where ' + #searchstring + '
for xml raw(''Data'')
)
'
exec sp_executesql
#stmt = #stmt,
#params = N'#Data xml output',
#Data = #Data output
select
T.C.value('#id', 'int') as id
from #Data.nodes('Data') as T(C)
sql fiddle demo
The following example creates a user-defined table type that has one Id column. Further the INSERT #RetIds EXEC(#STR) statement fills the parameter list, and then passes the values to a stored procedure
CREATE TYPE RetIds AS TABLE
(
Id int
)
DECLARE #searchstring varchar(500) = 'AND SearchCol = 1'
DECLARE #str varchar(max)
SELECT #str ='SELECT Id FROM dbo.test6 WHERE 1 = 1 ' + #searchstring
DECLARE #RetIds AS RetIds
INSERT #RetIds
EXEC(#str)
EXEC dbo.ExecIds #RetIds
See demo on SQLFiddle