T-SQL dynamic sql within while loop - sql-server

I would like to use T-SQL while loop to get var_1, var_2, var_3 individually at each loop. But, it returns error message "Must declare the scalar variable "#var_1","#var_2","#var_3". Could please help me out. Thank you. I attached my code below:
declare #var_1 varchar(max)
set #var_1 = 'abcdef'
declare #var_2 varchar(max)
set #var_2 = 'ghijk'
declare #var_3 varchar(max)
set #var_3 = 'lmnopq'
declare #counter tinyint
set #counter = 1
declare #termName varchar(max)
while #counter<=3
begin
set #termName = '#var_' + CONVERT(varchar(10), #counter)
print #termName
declare #sql_code varchar(max)
set #sql_code = '
print '+ #termName+';
'
print #sql_code
exec (#sql_code)
set #counter = #counter + 1
end

When you use EXEC with a string, the command is carried out in a new session, so variables cannot be used pass arguments or get results. However, you could create a temporary table, put the arguments in it and use this table inside the dynamic statement:
create table #T (val_1 varchar(10), val_2 varchar(10), val_3 varchar(10));
insert into #T values ('abcef', 'ghijk', 'lmnopq');
declare #counter tinyint
set #counter = 1
while #counter<=3
begin
declare #sql_code varchar(max)
set #sql_code = '
declare #v varchar(10);
select #v = val_' + CONVERT(varchar(10), #counter) + ' FROM #T;
print #v;
'
print #sql_code
exec (#sql_code)
set #counter = #counter + 1
end

Related

Set #Variable1 within a stored proc by executing #Variable2

I want to set the value of #Count by executing #Counter within a Begin Try of a stored procedure.
SET #Counter ='SET #Count = (SELECT COUNT(' + #COLUMN + ') FROM ' + #TABLE + ' WHERE CONVERT(VARCHAR(MAX),' + #COLUMN + ') = ''' + #DATATOFIND + ''')'
I have tested the above code and it does give me the expected result for populating the #Count variable inside of a normal sql statement outside of a stored procedure.
Once the #Count variable is populated I want to use it in a print statement.
PRINT '-- No. of Entries in the ' + #TABLE + ' Table = ' + #Count
I have tried to the following two options to get the #Count populated but neither has worked
EXEC #Counter
and
EXECUTE sp_executesql (#Counter)
UPDATE:
After some more research I tried this:
DECLARE #Counter NVARCHAR(1000)
SET #Counter = N'DECLARE #Count NVARCHAR(100); SET #COUNT = (SELECT COUNT(UserId) FROM UserGrp WHERE CONVERT(VARCHAR(MAX),UserId) = ''za02'')'
EXECUTE sp_executesql #Counter
Print #Count
But I receive this error:
Must declare the scalar variable "#Count"
UPDATE: Workaround / Solution to my situation
DECLARE #Counter NVARCHAR(2000)
SET #Counter = 'DECLARE #Count NVARCHAR(100); SET #COUNT = (SELECT COUNT(UserId) FROM UserGrp WHERE CONVERT(VARCHAR(MAX),UserId) = 'to01'); Print '/* No. of Entries in the UserGrp Table - ' + #Count + ' */''
EXEC (#Counter)
This gives me clear information in my result to decide what to do with the created code from the rest of the stored proc
Dynamic SQL requires careful handling:
DECLARE #Counter NVARCHAR(1000);
DECLARE #COUNT BIGINT;
DECLARE #DATATOFIND VARCHAR(100) = 'za02';
DECLARE #TABLE SYSNAME = N'UserGrp';
DECLARE #COLUMN SYSNAME = N'UserId';
SET #Counter = N'SELECT #COUNT = COUNT(<column_name>)
FROM <table_name>
WHERE CONVERT(VARCHAR(MAX),<column_name>) = #DATATOFIND;';
SET #Counter = REPLACE(#Counter, '<column_name>', QUOTENAME(#COLUMN));
SET #Counter = REPLACE(#Counter, '<table_name>', QUOTENAME(#TABLE));
PRINT #Counter; -- debug
EXECUTE sp_executesql #Counter,
N'#DATATOFIND VARCHAR(100), #COUNT BIGINT OUTPUT',
#DATATOFIND,
#COUNT OUTPUT;
SELECT #COUNT;
db<>fiddle demo
Minimum:
params are parameters, not concatenated string
identifiers(here column/table name) - should be quoted for instance using QUOTENAME function
it is good to print query to see if it doing what is expected
parameters set inside dynamic query could be passed to outer block by defining them as OUTPUT

Transform text in SQL Server

I am trying to create a dynamic query in SQL Server.
Input: #value= abc,def,en,
Output: MAX(abc) as abc, MAX(def) as def, MAX(en) as en
My efforts so far took me no where.
With CONVERT() and REPLACE() I achieved a bit but finding it difficult. Need help!
Try this:
declare #value varchar(50) = 'abc,def,en'
declare #result varchar(100) = ''
select #result = replace(#value,'abc', 'MAX(''abc'') as abc')
select #result = replace(#result,'def', 'MAX(''def'') as def')
select #result = replace(#result,'en', 'MAX(''en'') as en')
select #result
You can also do the replacements in one line by nesting the expressions.
EDIT: If you have variable values in #value, you can take the below approach:
Use a splitter function to get the individual values in the string as a list. You can take a look at this article for implementations.
Insert this list to a temp table.
Update the temp table as shown above.
Concatenate the values into a single string using STUFF like so:
select stuff((select ',' + val from #temp for xml path('')),1,1,'')
Try this:
DECLARE #Value VARCHAR(200) = 'abc,def,en'
DECLARE #Template VARCHAR(100) = 'MAX(''##'') as ##'
DECLARE #Result VARCHAR(1000) = ''
DECLARE #Data VARCHAR(100) = ''
WHILE LEN(#Value) > 0
BEGIN
SET #Data = REPLACE(LEFT(#Value, ISNULL(NULLIF(CHARINDEX(',', #Value),0), LEN(#Value))),',','')
SET #Result = #Result + REPLACE(#Template, '##', #Data)
IF CHARINDEX(',', #Value) > 0
BEGIN
SET #Result = #Result + ','
SET #Value = REPLACE(#Value,#Data + ',','')
END
ELSE
SET #Value = REPLACE(#Value,#Data,'')
END
SELECT #Result
Have a look at SQL User Defined Function to Parse a Delimited String
So you can do like
Declare #Value varchar(200) = 'abc,def,en'
Declare #Item varchar(20) = null
declare #Str varchar(1000)=''
WHILE LEN(#Value) > 0
BEGIN
IF PATINDEX('%,%',#Value) > 0
BEGIN
SET #Item = SUBSTRING(#Value, 0, PATINDEX('%,%',#Value))
-- SELECT #Item
IF(LEN(#Str)>0)
SET #Str = #Str + ', SELECT MAX('+#Item+') as ' +#Item
ELSE
SET#Str = #Str + ' SELECT MAX('+#Item+') as ' +#Item
SET #Value = SUBSTRING(#Value, LEN(#Item + ',') + 1, LEN(#Value))
END
ELSE
BEGIN
SET #Item = #Value
SET #Value = NULL
SET #Str = #Str + 'SELECT MAX('+#Item+') as ' + #Item
END
END
select #Str
See the fiddle sample here

Find column names in executed query sql server

I have a table that stores SQL queries. I retrieve a query according to a condition and store it in a variable:
---------------------------------------------------------
ID | Query
---------------------------------------------------------
1 | 'Select Id,Name from Student'
2 | 'Select Id,Name,Father_Name from Student'
3 | 'Select Id,Name,Email from Student_Detail'
....
---------------------------------------------------------
For example, a variable #sql might have the first query from above:
Declare #sql nvarchar(500)
set #sql = 'Select Id,Name from Student'
I execute this query using:
Exec(#sql)
The problem is, how do I know which columns are used in that query? I'm trying to achieve something like what ColdFusion does with query.ColumnList, which returns the column list used in that query.
Try this:
SELECT SUBSTRING(query,8,CHARINDEX('from',query)-9) AS ColumnList
FROM YourTable
this is a variant of mehdi lotfi solution but is equally weak in the sense that only gives you whatever is in between the select and from statements so if you have aliases or calculations or 'case' statements it will not work properly; it does work if the column list is straight forward comma separated columns:
SELECT LEFT(REPLACE(#SQL,'Select ',''), CHARINDEX(' from',REPLACE(#SQL,'Select ',''))) AS ColumnList
Finally i managed to solve at my own using this solution.
Declare #sql varchar(1000)
Declare #valueList nvarchar(500)
Declare #tbl Table(Name varchar(100))
Declare #selectPos Int
Declare #fromPos Int
Declare #len Int
Declare #pos Int
Declare #prevpos Int
Declare #Delimeter varchar(2)
set #sql = 'Select Id,Name,Father_Name from Student'
set #selectPos = CHARINDEX('Select ', #sql, 1)+7
set #fromPos = CHARINDEX('From ', #sql, 1)
set #len = #fromPos - #selectPos
set #valueList = SUBSTRING(#sql, #selectPos, #len)
set #Delimeter = ', '
set #pos = 1
set #prevpos = 0
while #pos > 0
Begin
set #pos = charIndex(#Delimeter, #valueList, #pos)
If #pos = 0
Begin
Insert into #tbl
Select SUBSTRING(#valueList,#prevPos + 1,LEN(#valueList) - #prevpos)
Break;
End
Insert into #tbl
Select SUBSTRING(#valueList,#prevPos + 1,#pos-#prevpos - 1)
set #prevpos = #pos
set #pos = #pos + 1
End
select * from #tbl

How to get "singular" values in WHERE clause from a string?

I have a variable #text varchar which has some values separated by a symbol, whatever I chose it to be. Ex:
declare #text varchar
set #text='John^Marry^Smith^Ane^Sue^'
I need to delete some data, but because it is a different server and database (a very long story), I must specify the in the WHERE clause, the values from my string, something like this:
Delete Employers where employer_name in ('John','Marry','Smith','Ane','Sue')
Can this be done? Most of all without any other objects, like procedures or functions?
Best regards, Bogdan
Simplest way: generate your SQL query as a string, using replace to form your in list, then execute it.
declare #sqlquery nvarchar(max)
set #sqlquery = 'Delete Employers where employer_name in (''' + replace(#text, '^', ''',''') + ''')'
EXEC sp_executesql #sqlquery
IF I understand your question correctly, then the answer is yes, just at you have stated it. You can use the following strip to turn a string into a table. If you declare the return table as a table variable, then you can roll it into your script as a
DELETE where EXISTS(....)
Create function [dbo].[atf_BarListToTable]
(#list ntext)
RETURNS #tbl TABLE (ListPosn int IDENTITY(1, 1) NOT NULL,
SString VARCHAR(1028) NOT NULL) AS
BEGIN
DECLARE #pos int
DECLARE #textpos int
DECLARE #ChunkLength smallint
DECLARE #str nvarchar(4000)
DECLARE #tmpstr nvarchar(4000)
DECLARE #leftover nvarchar(4000)
SET #textpos = 1
SET #leftover = ''
WHILE #textpos <= datalength(#list) / 2
BEGIN
SET #ChunkLength = 4000 - datalength(#leftover) / 2
SET #tmpstr = ltrim(#leftover + substring(#list, #textpos, #ChunkLength))
SET #textpos = #textpos + #ChunkLength
SET #pos = charindex('|', #tmpstr)
WHILE #pos > 0
BEGIN
SET #str = substring(#tmpstr, 1, #pos - 1)
INSERT #tbl (SString) VALUES( #str)
SET #tmpstr = ltrim(substring(#tmpstr, #pos + 1, len(#tmpstr)))
SET #pos = charindex('|', #tmpstr)
END
SET #leftover = #tmpstr
END
IF ltrim(rtrim(#leftover)) <> ''
INSERT #tbl (SString) VALUES(#leftover)
RETURN
END

how to assign the integer value to nvarchar or varchar datatype in stored procedure

how to assign the integer value to nvarchar or varchar datatype in stored procedure
DECLARE #SQLString nvarchar(max)
SET #SQLString = N'declare #Identifier int;
SELECT COUNT(*) FROM ' + #batch+' where Identifier = #Identifier'
i need to check whether the #SQLString is 0 or not.
i.e i want to check -----> if(#SQLString =0). how to assign a integer val to nvarchar or varchar
You could try something like
DECLARE #IntVal INT,
#ParamDef NVARCHAR(MAX),
#SQLString nvarchar(max),
#batch VARCHAR(MAX)
SELECT #batch = 'Batch',
#SQLString = N'SELECT #IntVal = COUNT(*) FROM ' + #batch,
#ParamDef = '#IntVal INT OUTPUT'
EXECUTE sp_executesql #SQLString,#ParamDef, #IntVal=#IntVal OUT
SELECT #IntVal
Have a look at sp_executesql (Transact-SQL)
I think this way is best:
DECLARE
#Cnt int,
#SQL nvarchar(max),
#batch sysname,
#Identifier varchar(30)
-- set #batch and #Identifier
SET #SQL = 'SELECT #Cnt = Count(*) FROM ' + #batch
+ ' WHERE Identifier = #Identifier'
EXEC sp_executesql #SQL, N'#Cnt int OUT, #Identifier varchar(30)',
#Cnt OUT, #Identifier
IF #Cnt = 0 BEGIN
--do something
END
ELSE BEGIN
--do something else
END
Though if you only care whether it's 0 or not, then you should do this instead, which can stop after finding only one row, instead of having to count them all:
DECLARE
#HasRows bit,
#SQL nvarchar(max),
#batch sysname,
#Identifier varchar(30)
-- set #batch and #Identifier
SET #SQL = 'SET #HasRows = CASE WHEN EXISTS (SELECT 1 FROM '
+ #batch + ' WHERE Identifier = #Identifier) THEN 1 ELSE 0 END'
EXEC sp_executesql #SQL, N'#HasRows bit OUT, #Identifier varchar(30)',
#HasRows OUT, #Identifier
IF #HasRows = 0 BEGIN
--do something
END
ELSE BEGIN
--do something else
END
However, if there's any way you can avoid using dynamic SQL and changing table names, that would be best. Then it is a simple query:
IF NOT EXISTS (SELECT 1 FROM TableName WHERE Identifier = #Identifier) BEGIN
-- do something
END
You're setting #SQLString to a query... if you want to see if it's '0', then you can just do:
IF #SQLString = '0'
But I think you're trying to find out if there are 0 rows in your batch, in which case, you mean something more like:
DECLARE #Res TABLE (cnt int);
INSERT #Res exec sp_executesql #SQLString;
IF (SELECT MAX(cnt) FROM #Res) = 0 /* Means empty batch */
convert it:
SET #var = CONVERT(VARCHAR, #intval)
Sascha
I am assuming that you are trying to check how many rows the dynamic sql returned you can do something like this:
DECLARE #SQLString nvarchar(max)
SET #SQLString = N'declare #Identifier int;
SELECT * FROM ' + #batch+' where Identifier = #Identifier'
exec #SQLString
set #SQLString = ##ROWCOUNT
if #SQLString = 0
begin
...
end

Resources