Iterating through columns with conditionals - sql-server

I'm currently trying to iterate through 38 columns titled 'Switch 1, Switch 2, ....' in order to update them based on a condition.
Here's my code:
DECLARE #i int
DECLARE #selec nvarchar(max)
SET #i = 1
WHILE # i <= 38
BEGIN
SET #selec = 'UPDATE 'Catalog v4'' + '
SET 'Switch' + LTRIM(STR(#i+1)) = ' + CASE
WHEN ( 'Switch' + LTRIM(STR(#i+1))= [Switch Check String] ) THEN ( '' )
ELSE ( 'Switch' + LTRIM(STR(#i+1)))
SET #i = #i+1
EXEC(#selec)
END
I keep getting an error
Must declare scalar variable #, incorrect syntax near Recipe
Any help would be very much appreciated, thanks!

WHILE # i <= 38
Should Be:
WHILE #i <= 38

Assuming you are trying to update table [Catalog 4], following might help:
DECLARE #i int
DECLARE #selec nvarchar(max)
SET #i = 1
WHILE #i <= 38
BEGIN
SET #selec = 'UPDATE '+QUOTENAME('Catalog v4')+'
SET '+QUOTENAME('Switch ' + LTRIM(STR(#i+1)))+' = CASE
WHEN ('+QUOTENAME('Switch '+ LTRIM(STR(#i+1)))+' = ''[Switch Check String]'' ) THEN ( '''' )
ELSE ('+QUOTENAME('Switch ' + LTRIM(STR(#i+1)))+') END'
PRINT #selec --please check the printed messages before executing those.
SET #i = #i+1
--EXEC(#selec)
END
Quotename helps in defining object names correctly if there are spaces in them.
Please replace the [Switch check string] accordingly.

The following code creates a template statement, then updates it each time through the loop rather than trying to assemble it from bits'n'pieces each time.
declare #SQLTemplate as VarChar(256) =
'update [Catalog V4] set Switch# = '' where Switch# = [Switch Check String];'
declare #SQL as VarChar(256);
declare #Index as Int = 1;
while #Index <= 38
begin
set #SQL = Replace( #SQLTemplate, '#', Cast( #Index as VarChar(3) ) );
execute ( #SQL );
set #Index += 1;
end
Note that the update statement has been simplified to avoid updating every row on every pass, regardless of whether the value actually changes.

Another approach (requires thorough testing) is to use the Information.Schema.Columns View instead of a loop.
declare #select varchar(4000) = ''
select
#select = #select
+ 'update YourTableName set ' + COLUMN_NAME + ' = ' + ' CASE WHEN ' + COLUMN_NAME
+ ' = ''[Switch Check String]'' THEN '''' ELSE ' + COLUMN_NAME + 'END ; '
FROM
INFORMATION_SCHEMA.COLUMNS
where
TABLE_NAME = 'YourTableName'
and COLUMN_NAME like 'YourCondition'
print #select
--exec #select

Related

Create INSERT INTO statement passing a variable list like list of the column

I have this little statement to extract the list of the column of a table:
DECLARE #j INT = 1
DECLARE #verifyColumn INT = #pkcounter --it count the number of column of the table
WHILE #j <> #verifyColumn +1
BEGIN
set #multipleColumn = #multipleColumn+ (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMN WHERE TABLE_SCHEMA = #schema_name AND TABLE_NAME = #table_source AND ORDINAL_POSITION = #k) + ','
SET #j = #j + 1
set #k = #k + 1
SET #pkcounter = #pkcounter - 1;
END
PRINT 'multipleColumn: ' + #multipleColumn
Now i need to use the variable with the list of the column in an insert statement, a sort of:
INSERT INTO table_name_target (#multipleColumn) SELECT a,b FROM table_name_source
It's possible to do that?
Thank you all
No, you must use sp_executesql. Like this:
DECLARE #multipleColumn VARCHAR(1000) = NULL, -- Must be null
#schema_name NVARCHAR(128)='dbo',
#table_source NVARCHAR(128)='<src>',
#table_target NVARCHAR(128)='<dest>',
#stmt NVARCHAR(MAX);
SELECT #multipleColumn = COALESCE(#multipleColumn+', ', '') + QUOTENAME(c.COLUMN_NAME)
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE
c.TABLE_SCHEMA = #schema_name
AND c.TABLE_NAME = #table_source;
SET #stmt=CONCAT('INSERT INTO ', QUOTENAME(#table_target),' (', #multipleColumn, ')
SELECT ', #multipleColumn, ' FROM ', QUOTENAME(#table_source), ';');
EXEC sp_executesql #stmt;
And, as always, be careful of sql injection when using sp_executesql!

Merging more than one table into one existing table

This is the table creation and insertion query
If not exists(select * from sysobjects where name='hrs')
Create table hrs(hr int)
declare #cnt int =1
while #cnt <= 12
begin
insert into hrs values(#cnt)
set #cnt=#cnt+1
end
The above code gives the output like
but I just want that
declare #cnt1 int = 1
while #cnt1<=12
begin
EXEC('select he'+#cnt1+' = case when hr = 1 then '+#cnt1+' end from hrs')
set #cnt1=#cnt1+1
end
The above code returns the 12 different table but i just want the all records in one table (without creating any new table).
So, how can i do this?
Please help me.
Thanks.
Here the all column are created dynamically through loop
Here are the full query
declare #s varchar(MAX)=''
declare #j int = 1
while #j<=12
begin
if #j = 12
Set #s = #s+'he'+convert(varchar,#j)+'=MAX(case when hr='+convert(varchar,#j)+' then '+convert(varchar,#j)+' end)'
else
set #s = #s+'he'+convert(varchar,#j)+'=MAX(case when hr='+convert(varchar,#j)+' then '+convert(varchar,#j)+' end),'
set #j=#j+1
end
set #s = 'select '+#s+' from hrs'
exec(#s)
Your query doesn't make a lot of sense, but you can build a list of columns and then exec that:
declare #columns nvarchar(max)
declare #cnt int = 1
while #cnt <= 12
begin
set #columns = isnull(#columns + ', ', '') + 'He' + cast(#cnt as nvarchar) +
' = sum(case when hr = ' + cast(#cnt as nvarchar) + ' then hr end)'
end
declare #sql nvarchar(max) = 'select ' + #columns ' + from hr'
exec (#sql)

SQL Server : how to remove consecutive duplicate words in a string

Currently I have a dynamic query, generated in the stored procedure, that has a bug. Somehow there is a consecutive duplicate 'AND' generated in it.
Wondering if there is a way to delete the CONSECUTIVE DUPLICATE 'AND' from the dynamic query string.
For eg:
var str = 'Select * from employee A where A.age > 30 AND AND A.role = ''developer'''
Update
The replace as suggested below doesnt work
Please see the below query:
DECLARE
#str NVARCHAR(MAX)
SET #str = 'fasdf asdfasf asfasdfasafsdf AND AND asdfasdfasd AND dfasdfa'
SET #str = REPLACE(#str, 'AND AND', 'AND')
PRINT #str
Thanks!
Something like this?
/****** Object: StoredProcedure [dbo].[RemoveConsecutiveDuplicateTokens] Script Date: 30/06/2016 09:30:50 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[RemoveConsecutiveDuplicateTokens]
#instr varchar(max) ,
#outstr varchar(max) OUTPUT
as
declare #workstr varchar(max) = ltrim(#instr),
#newstr varchar(max),
#oldtoken varchar(max),
#newtoken varchar(max)
while #workstr is not null
begin
if #oldtoken is null
begin
set #oldtoken = SUBSTRING(#workstr,1,charindex(' ',#workstr))
set #workstr = ltrim(Stuff(#workstr, CharIndex(#oldtoken,#workstr), Len(#oldtoken), ''))
set #newstr = #oldtoken + ' '
end
set #newtoken = SUBSTRING(#workstr,1,charindex(' ',#workstr))
if #newtoken <> #oldtoken
begin
if #newtoken <> char(39)
begin
set #oldtoken = #newtoken
set #newstr = #newstr + #newtoken + ' '
set #workstr = ltrim(Stuff(#workstr, CharIndex(#newtoken,#workstr), Len(#newtoken), ''))
end
end
else
begin
set #workstr = ltrim(Stuff(#workstr, CharIndex(#newtoken,#workstr), Len(#newtoken), ''))
end
if charindex(' ',#workstr) = 0
break
end
set #newtoken = SUBSTRING(#workstr,1,len(#workstr))
if #newtoken <> #oldtoken
begin
if #newtoken <> char(39)
begin
set #oldtoken = #newtoken
set #newstr = #newstr + #newtoken + ' '
set #workstr = ltrim(Stuff(#workstr, CharIndex(#newtoken,#workstr), Len(#newtoken), ''))
end
end
else
begin
set #workstr = ltrim(Stuff(#workstr, CharIndex(#newtoken,#workstr), Len(#newtoken), ''))
end
select #outstr = #newstr
return
First of all, you are doing it wrong. Fix the logic that generates this incorrect sql.
But for research/learning purposes, this is how you do.
REPLACE ( str , 'AND AND' , 'AND')
I forgot how much I dislike SUBSTRING, but then that has been my struggle to read <starting_position> as truly the position the value begins with.
However, the real beast was how string manipulations are implemented in SQL Server under the context of the ##TRANCOUNT.
Consider the statement
PRINT QUOTE_NAME(REPLACE('My____Table', '__', '_'))
We wish to use proper naming standards, but the function returns:
`[My__Table]`
Why? Because REPLACE jumps ahead the length of the duplicates. To prove it, lets add one more '_' CHAR(95) and we get this in return:
`[My___Table]`
So then simply embedding this with a WHILE statement, for example, will be quite sufficient for our needs. Note I replaced the spaces with '_' for readability
DECLARE #instr varchar(max)
SET #instr = 'SELECT * from employee A where A.age > 30 AND AND A.role = ''developer'''
DECLARE #workstr varchar(max) = REPLACE(LTRIM(#instr), ' ', '_'),
#tokenque VARCHAR(MAX),
#newstr INT = 0,
#token varchar(max),
#flag_break INT = 0
-- removes the extra "spaces"
WHILE CHARINDEX('__', #workstr) <> 0
BEGIN
SET #workstr = REPLACE(#workstr, '__' , '_')
END
SET #tokenque = #workstr
WHILE (CHARINDEX('_', #tokenque) <> 0)
BEGIN
SET #token = SUBSTRING(#tokenque, 1, CHARINDEX('_', #Tokenque) - 1 )
IF #token <> '''' -- (') delimiter skipped
BEGIN
WHILE CHARINDEX(#token + '_' + #token, #workstr) <> 0
BEGIN
SET #workstr = REPLACE(#workstr, #token + '_' + #token, #token)
END
SET #tokenque = SUBSTRING(#tokenque, LEN(#token) + 2, LEN(#tokenque) )
END
ELSE SET #tokenque = SUBSTRING(#tokenque, LEN(#token) + 2, LEN(#tokenque) )
PRINT #tokenque --if you want to see the progression
END
PRINT REPLACE(#workstr, '_', ' ')
RESULT:
'SELECT * from employee A where A.age > 30 AND A.role = 'developer'
use the REPLACE function and replace 'AND AND' by 'AND'. example
SELECT REPLACE('Select * from employee A where A.age > 30 AND AND A.role = ''developer'' ','AND AND','AND');

T-SQL string email format issue

I am trying to build an email and have run into an issue. When the stored procedure runs, I get the following error message.
Msg 14624, Level 16, State 1, Procedure sp_send_dbmail, Line 242
At least one of the following parameters must be specified. "#body,
#query, #file_attachments, #subject".
My code is below but I am adding each of the requested items. I have narrowed down where the breakdown happens. If I pull out the concatenation "+" everything works as expected. But I have done this before with the concatenation so I am not sure what is different.
DECLARE #RespPeriod varchar(20)
DECLARE #SubjectLine varchar(100)
DECLARE #ContactEmail varChar(100)
DECLARE #AAEAPVSupplierID int
DECLARE #key varchar(50)
DECLARE #formattedURL varchar(100)
DECLARE #emailBody varchar(max)
DECLARE Curs Cursor
FOR
SELECT theID FROM #temptbl
OPEN Curs
FETCH NEXT FROM Curs INTO #theID
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT *
INTO #temptbl
FROM tblmainTbl
WHERE theID = #theID
DECLARE #isComplete Bit = 1
IF EXISTS (SELECT * FROM #temptbl WHERE Complete = 0)
BEGIN
SET #isComplete = 0
END
IF #isComplete = 1
BEGIN
SET #SubjectLine = 'Testing ' + #RespPeriod + ' Testing.'
SET #ContactEmail = (SELECT SalesEmail FROM #temptbl WHERE theID = #theID)
SET #key = (SELECT ResponseKEY FROM #temptbl WHERE theID = #theID)
SET #formattedURL = 'http://www.something.com/something.aspx?rkey=' + #key
SET #emailBody = '<html>Dear BlaBlaBla' + #RespPeriod + ' ' + #formattedURL + '">' + #formattedURL + '</a></html>'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'SMTPProfile'
,#recipients = #ContactEmail
,#subject = #SubjectLine
,#body = #emailBody
,#body_format = 'HTML'
END
DROP TABLE #temptbl
FETCH NEXT FROM Curs INTO #theID
END
CLOSE Curs
DEALLOCATE Curs
Your code sample is incomplete (you're lacking the declaration of some of the variables used). My hunch is one or more of the variable values (maybe #RespPeriod?) is NULL, and when you do the concatenations for the variable assignments used in your sp_send_dbmail call, you're passing NULL.
Remember, string + NULL = NULL
Right before your call to the sp_send_dbmail, insert these statements...
PRINT '--------------'
PRINT '#SubjectLine = ' + ISNULL(#SubjectLine, 'NULL')
PRINT '#ContactEmail = ' + ISNULL(#ContactEmail, 'NULL')
PRINT '#key = ' + ISNULL(#key, 'NULL')
PRINT '#formattedURL = ' + ISNULL(#formattedURL, 'NULL')
PRINT '#emailBody = ' + ISNULL(#emailBody, 'NULL')
PRINT '--------------'
It should quickly become apparent if this is your cause. If it is, chase back the individual parts of whatever variables are resolving as NULL until you find the piece that caused the entire string to be NULL. If it is not, please provide more code so we can look somewhere else.

Msg 137, Level 15, State 2, Line 29 Must declare the scalar variable "#ACTIVE_STATUS"

ALTER PROCEDURE [dbo].[S_EDIT_USER] (#DSA_CODE VARCHAR(10),
#REQUESTOR_DEPT VARCHAR(40),
#ACTIVE_STATUS INT,
#MAKER_ID VARCHAR(10),
#MAKER_IP VARCHAR(20),
#ERROR_CODE INT OUTPUT)
AS
BEGIN
DECLARE #CNT INT;
DECLARE #SQL NVARCHAR(MAX);
SELECT #CNT = COUNT(*)
FROM TMAS_UAM_USER_TMP
WHERE DSA_CODE = #DSA_CODE;
IF #CNT > 0
SET #ERROR_CODE = 1;
ELSE
SET #ERROR_CODE = 0;
IF #REQUESTOR_DEPT = 'N'
SET #REQUESTOR_DEPT = '';
ELSE
SET #REQUESTOR_DEPT = #REQUESTOR_DEPT;
PRINT #REQUESTOR_DEPT;
IF #ERROR_CODE = 0
SET #SQL = 'INSERT INTO TMAS_UAM_USER_TMP (
DSA_CODE
,DSA_NAME
,DSA_CITY
,DSA_PRODUCT
,DSA_PHNO
,DSA_MOBNO
,DSA_RQSTR
,DSA_RQSTR_DEPT
,GROUP_ID
,ACTIVE_STATUS
,REQ_TYPE
,LAST_LOGED_IN
,CREATED_ID
,CREATED_IP
,CREATED_DATE
,MAKER_ID
,MAKER_IP
,MAKER_DATE
) SELECT DSA_COD
,DSA_NAM
,DSA_CTY
,PRODUCT
,DSA_PHO
,DSA_MOB
,REQUESTOR
,' + #REQUESTOR_DEPT + '
,GROUP_ID
,#ACTIVE_STATUS
,1
,LAST_LOG_DAT
,CREATED_ID
,CREATED_IP
,CREATED_DATE
,' + #MAKER_ID + '
,' + #MAKER_IP + '
,GETDATE()
FROM DSA_MST WHERE DSA_COD = ' + #DSA_CODE + ' and ';
IF #REQUESTOR_DEPT = 'N'
BEGIN
SET #SQL = #SQL + 'REQUESTOR_DEPT is null';
PRINT( 'If Query' + #SQL );
END
ELSE
BEGIN
SET #SQL = #SQL + 'REQUESTOR_DEPT = ''' + #REQUESTOR_DEPT + '''';
PRINT( 'Else Query' + #SQL );
END
EXECUTE (#SQL);
RETURN #ERROR_CODE;
END
The outer variables and parameters are not in scope for your EXECUTE (#SQL);
You need to use sp_executesql instead and pass them in as parameters.
Also you should read up on SQL injection. You might be vulnerable if parameters such as #REQUESTOR_DEPT originate from untrusted sources such as user input as you are just concatenating them straight into the query.

Resources