SQL Cursor within Stored Procedure to populate string variable - sql

I have a stored procedure that contains a cursor to loop through SQL records and populates the string which I will use later as my email text. I'm trying to print it out to verify before I can proceed with it but it seems to not populate the string. Here is my stored procedure in SQL Server 2005.
CREATE PROCEDURE [dbo].[spBody]
AS
DECLARE #MyCursor CURSOR
DECLARE #emailBody nvarchar(max)
DECLARE #statusName nvarchar(max)
DECLARE #deptCode nvarchar(max)
DECLARE #instructors nvarchar(max)
DECLARE #meetingTime nvarchar(max)
SET #MyCursor = CURSOR FAST_FORWARD For
Select StatusName, DeptCode, Instructors, Description from MyTable where StatusID = (select CAST(value AS INT) from Table2 where ConfigOption = 'RequiredStatus')
Open #MyCursor
FETCH NEXT FROM #MyCursor INTO #statusName, #deptCode, #instructors, #meetingTime
WHILE ##FETCH_STATUS = 0
BEGIN
SET #emailBody = #emailBody + #statusName + ' ' + #deptCode + ' ' + #instructors + ' ' + #meetingTime
FETCH NEXT FROM #MyCursor INTO #statusName, #deptCode, #instructors, #meetingTime
END
CLOSE #MyCursor
Print #emailBody
DEALLOCATE #MyCursor

It's because #emailBody starts out as NULL, and any concatenation with NULL yields NULL by default. Do a
SET #emailBody = '';
at the beginning of your script.
Also, strongly consider adding a SET NOCOUNT ON; statement at the top of your stored procedure -- not having NOCOUNT ON can greatly slow the execution of your proc.

Why do you need a cursor for this string concat. Wont the following query suffix
DECLARE #emailBody nvarchar(max)
Set #emailBody = ''
Select #emailBody = #emailBody + StatusName + ' ' + DeptCode + ' ' + Instructors + ' ' + [Description] from MyTable where StatusID = (select CAST(value AS INT) from Table2 where ConfigOption = 'RequiredStatus')
Print #emailBody

Related

Update specific column in all tables base on specific value

I'm trying to update all table based on my value. This is a script Task in my SSIS package.
But I get the error:
Multiple-step OLE DB operation generated errors. Check each OLE DB status value, if available. No work was done.
Code looks like this:
DECLARE #Column_name varchar(MAX)
DECLARE #Column_Datatype varchar(max)
SET #COLUMN_NAME='site_id'
SET #COLUMN_DATATYPE='varchar(10)'
------------------------------------------------Code---------------------------------------------------
GO
--Declare Variables
DECLARE #TableName VARCHAR(100)
DECLARE #TableSchema VARCHAR(100)
DECLARE #COLUMN_NAME VARCHAR(50)
DECLARE #COLUMN_NAME_UPDATE VARCHAR(50)
SET #COLUMN_NAME_UPDATE = ?
SET #COLUMN_NAME='site_id'
DECLARE #COLUMN_DATATYPE VARCHAR(50)
SET #COLUMN_DATATYPE='varchar(max)'
--Declare Cursor
DECLARE CUR CURSOR FOR
SELECT TABLE_SCHEMA,
TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
--OPEN CURSOR
OPEN CUR
--Fetch First Row
FETCH NEXT FROM CUR INTO #TableSchema,#TableName
--Loop
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(MAX)
SET #SQL= ( SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME=#TableName AND COLUMN_NAME=#COLUMN_NAME
and Table_Schema=#TableSchema)
BEGIN
SET #SQL='UPDATE '+ #TableSchema+'.'+ '[' + #TableName + ']' + ' SET '+#COLUMN_NAME + '='
+ '''' + #COLUMN_NAME_UPDATE + '''' + 'WHERE site_id IS NULL '
PRINT #SQL
EXEC ( #SQL)
END
FETCH NEXT FROM CUR INTO #TableSchema,#TableName
END
--Close and Deallocate Cursor
CLOSE CUR
DEALLOCATE CUR
I use a variable like input.
What's wrong?

Loop through Query and Update field

I'm trying to loop through a fields defined in a query to an update statement.
I have the following SQL:
Declare #SQL varchar(max)
#SQL= 'Select [a],[b],[c],[d],[e]....[z]
From Table1;'
I want to be able to loop through all the fields [a]-[z] and update via the following statement:
Update Table 1
Set [a] = Case when [a] = 'Not at all' Then 0
when [a] = 'Very Much' Then 10 End
Field names are not actually [a]..[z]; I can't run the the update statement on the whole table, only a specific set of field names.
Struggling to write it programatically in SQL Server.
Declare #SQL varchar(max)
Declare #name varchar(100)
DECLARE #getid CURSOR
Set #getid = cursor for
SELECT name
FROM
sys.dm_exec_describe_first_result_set('Select [a],[b],[c],[d],[e]....[z]
From Table1', NULL, 0)
Open #getid
FETCH NEXT
FROM #getid INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'Update Table1
Set ' + #name + ' = Case when ' + #name +'= ''Very Much'' Then ''10''
when ' + #name + ' = ''Not at all'' Then ''0''
Else ' + #name + ' End'
Exec(#SQL)
FETCH NEXT
FROM #getid INTO #name
END
CLOSE #getid
DEALLOCATE #getid
Basically dm_exec_describe_first_result_set is grabbing the fieldnames and outputting it as a recordset. Then we are just passing the the each of the records to #name and use it form our update statement and then executing it for each record passed.
Hope this helps someone else! Curious to see if there is a better way.
I think if you want to make it a little more generic I would do something like the following code. This will allow you to not have to write the specific query for every table you want to do this to and you could potentially filter out columns you do not want in the future.
To be clear, I borrowed the SQL to do the actual UPDATE from #Dale-K post and just made it pretty.
DECLARE #strSQL NVARCHAR(1000)
DECLARE #strTable NVARCHAR(100)
DECLARE #strColName VARCHAR(100)
SET #strTable = N'Table1'
CREATE TABLE #COLUMNS(ColName varchar(100))
SET #strSQL = ' select COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = #TableName and DATA_TYPE in (''nvarchar'', ''varchar'')'
INSERT INTO #COLUMNS
EXEC sp_executeSQL #strSQL, N'#TableName nvarchar(100)', #TableName = #strTable
DECLARE csrColumns CURSOR LOCAL FORWARD_ONLY FOR
SELECT ColName FROM #COLUMNS
OPEN csrColumns
FETCH csrColumns INTO #strColName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #strSQL = N'UPDATE ' + #strTable + '
SET ' + #strColName + ' = CASE WHEN ' + #strColName +'= ''Very Much'' THEN ''10''
WHEN ' + #strColName + ' = ''Not at all'' THEN ''0''
ELSE ' + #strColName + ' END'
exec sp_ExecuteSQL #strSQL
FETCH csrColumns INTO #strColName
END
CLOSE csrColumns
DEALLOCATE csrColumns

Implementing a for loop in SQL to update rows

How do I write the below code in SQL? The query would update each column in the table from the result.
DECLARE #table_list NVARCHAR -- What data type do I use to hold a list?
DECLARE #column_list NVARCHAR
SET #table_list = (SELECT DISTINCT [ID_TABLE_NAME] FROM dbo.VMO) -- there are more than one results
SET #column_list = (SELECT DISTINCT [USR_COL_NAME] FROM dbo.VMO) -- there are more than one results
foreach(#table IN #table_list)
{
foreach(#column IN #column_list)
{
UPDATE #table
SET #column = '101211'
WHERE #column = '10120'
}
}
You can use CURSORs to act like a foreach loop:
DECLARE #table NVARCHAR(255)
DECLARE #column NVARCHAR(255)
DECLARE OUTER_CURSOR CURSOR
FOR SELECT DISTINCT [ID_TABLE_NAME] FROM dbo.VMO
OPEN OUTER_CURSOR
FETCH NEXT FROM OUTER_CURSOR INTO #table
WHILE (##FETCH_STATUS <> -1)
BEGIN
DECLARE INNER_CURSOR CURSOR
FOR SELECT DISTINCT [USR_COL_NAME] FROM dbo.VMO
OPEN INNER_CURSOR
FETCH NEXT FROM INNER_CURSOR INTO #column
WHILE (##FETCH_STATUS <> -1)
BEGIN
DECLARE #strQuery NVARCHAR(MAX)
SET #strQuery = 'UPDATE [' + #table + '] SET [' + #column + '] = ''101211'' WHERE [' + #column + '] = ''10120'';'
EXEC(#strQUERY)
FETCH NEXT FROM INNER_CURSOR INTO #column
END
CLOSE INNER_CURSOR
DEALLOCATE INNER_CURSOR
FETCH NEXT FROM OUTER_CURSOR INTO #table
END
CLOSE OUTER_CURSOR
DEALLOCATE OUTER_CURSOR
T-SQL doesn't have a FOR loop, perhaps it has a WHILE (Transact-SQL)
Use SQL like below:
DECLARE #table_list NVARCHAR -- What data type do I use to hold a list?
DECLARE #column_list NVARCHAR
SET #table_list = (SELECT DISTINCT [ID_TABLE_NAME] FROM dbo.VMO) -- there are more than one results
SET #column_list = (SELECT DISTINCT [USR_COL_NAME] FROM dbo.VMO) -- there are more than one results
WHILE #column_list > #table_list
BEGIN
UPDATE #table
SET #column = '101211'
WHERE #column = '10120'
END;

SQL cursor variable can not use in conditions

any idea why my #ProductNumber cursor variable is not working if used in dynamic statement? It says "Must declare scalar variable". But this is a cursor variable. But when I used it not dynamic, it works. Thanks.
DECLARE #TargetDB NVARCHAR(50)
DECLARE #SourceDB NVARCHAR(50)
DECLARE #DateEffectiveFrom datetime
DECLARE #InsertRecords NVARCHAR(1000)
SET #TargetDB = 'MySSISDb'
SET #SourceDB = 'MySSISDb2'
SET #DateEffectiveFrom = '2013-12-29'
Declare #ProductNumber INT;
DECLARE #SqlDb NVARCHAR(Max)
DECLARE Cur1 CURSOR FOR SELECT CAST(#SqlDb AS NVARCHAR(MAX))
SET #SqlDb = 'SELECT ProductNumber From '+#SourceDB+'.dbo.Item (NOLOCK)
WHERE NOT EXISTS (SELECT 1 FROM '+#TargetDB+'.dbo.Item2
WHERE ProductNumber = '+#SourceDB+'.dbo.Item.ProductNumber)'
EXEC sp_executesql #SqlDb
OPEN Cur1
FETCH NEXT FROM Cur1 INTO #ProductNumber;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #InsertRecords = 'INSERT INTO ' + #TargetDB + '.dbo.Item2(
ProductNumber, ProductName, ListPrice, BirthDate)
SELECT ProductNumber, ProductName,
ListPrice, ''' + CONVERT(nvarchar(25),#DateEffectiveFrom,120) + '''
FROM ' + #SourceDB + '.dbo.Item
WHERE ' + #SourceDB + '.dbo.Item.ProductNumber = #ProductNumber'
--WHERE ' + #SourceDB + '.dbo.Item.ProductNumber = #ProductNumber
--(this is where the problem)
EXEC sp_executesql #InsetRecords
FETCH NEXT FROM Cur1 INTO #ProductNumber;
END
CLOSE Cur1;
DEALLOCATE Cur1;
Shift the declaration of your cursor into the SQLDb variable. In SQL Server, at least, the cursor designation "Cur1" is accessible after the execution of #SQLDb. Also, I've moved the use of #ProductNumber to outside the literal string and fixed a typo on your second EXEC statement within the cursor while loop (#InsetRecords to #InsertRecords).
DECLARE #TargetDB NVARCHAR(50)
DECLARE #SourceDB NVARCHAR(50)
DECLARE #DateEffectiveFrom datetime
DECLARE #InsertRecords NVARCHAR(1000)
SET #TargetDB = 'MySSISDb'
SET #SourceDB = 'MySSISDb2'
SET #DateEffectiveFrom = '2013-12-29'
Declare #ProductNumber INT;
DECLARE #SqlDb NVARCHAR(Max)
SET #SqlDb = '
DECLARE Cur1 CURSOR FOR
SELECT ProductNumber
From '+#SourceDB+'.dbo.Item (NOLOCK)
WHERE NOT EXISTS (
SELECT 1
FROM '+#TargetDB+'.dbo.Item2
WHERE ProductNumber = '+#SourceDB+'.dbo.Item.ProductNumber)'
EXEC sp_executesql #SqlDb
OPEN Cur1
FETCH NEXT FROM Cur1 INTO #ProductNumber;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #InsertRecords = 'INSERT INTO ' + #TargetDB + '.dbo.Item2(
ProductNumber, ProductName, ListPrice, BirthDate)
SELECT ProductNumber, ProductName,
ListPrice, ''' + CONVERT(nvarchar(25),#DateEffectiveFrom,120) + '''
FROM ' + #SourceDB + '.dbo.Item
WHERE ' + #SourceDB + '.dbo.Item.ProductNumber = ' + CONVERT(nvarchar(max), #ProductNumber)
EXEC sp_executesql #InsertRecords
FETCH NEXT FROM Cur1 INTO #ProductNumber;
END
CLOSE Cur1;
DEALLOCATE Cur1;

Find specific word in all rows in MS SQL database and eventually replace it

Is there a way to scan all tables in MS SQL 2008 R2 Database and replace one word to another? Or if replace is not possible maybe just possibility to list all rows with specific word (and corresponding table next to it for reference)?
If it's not possible to do purely in SQL then I can use C# as well.
There is no "out of the box" solution for this, but it's not very hard to write a stored procedure that does this.
For example, the procedure below will loop over all the tables and then loop over all the columns of type varchar and nvarchar and replace the string #value with #newvalue. This is just a proof of concept and can be enhanced greatly to make it faster by adding a where clause that checks if the string contains the value for example. (with LIKE or using full text indexes).
create proc ReplaceStrings(
#value nvarchar(maX)
, #newvalue nvarchar(max)
)
AS
declare #table_id int
, #name sysname
, #fieldname sysname
, #sql nvarchar(max)
, #fields nvarchar(max)
if #value = ''
begin
raiserror('The search value can not be empty', 16, 1)
return (-1)
end
declare tab cursor read_only local
for
select object_id, name from sys.tables
open tab
fetch next from tab into #table_id, #name
while ##FETCH_STATUS = 0
begin
SELECT #sql = N'UPDATE ' + QUOTENAME(#name) + '
set '
, #fields = NULL
declare field cursor read_only local
for
select name from sys.columns where object_id = #table_id and system_type_id in (type_id('varchar'), type_id('nvarchar'))
open field
fetch next from field into #fieldname
while ##FETCH_STATUS = 0
begin
set #fields = coalesce(#fields + ',', '') + N' ' + quotename(#fieldname) + ' = REPLACE(' + quotename(#fieldname) + ', #value, #newvalue)'
fetch next from field into #fieldname
end
close field
deallocate field
set #sql += #fields
print #sql
exec sp_executesql #sql , N'#value nvarchar(max), #newvalue nvarchar(max)', #value, #newvalue
fetch next from tab into #table_id, #name
end
close tab
deallocate tab
return (0)
Call the procedure like this:
exec ReplaceStrings 'haha', 'hihi'