Loop through Query and Update field - sql

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

Related

Update null value to a column in dynamic SQL

I need to update a specific set of columns with null value, but when I'm trying to pass null value to dynamic SQL I'm not getting any error or output.
DECLARE #Value VARCHAR(100)
SELECT #Value = null
DECLARE #TableName VARCHAR(1000),#ColumnName VARCHAR(100)
DECLARE #Sql NVARCHAR(MAX)
SET #Sql= N''
DECLARE UpdatePlantId_Crsr CURSOR
STATIC FOR
SELECT ST.name AS TableName,SC.name AS ColumnName
FROM
sys.columns SC
INNER JOIN
sys.tables ST ON ST.object_Id = SC.Object_Id
WHERE
SC.name like '%_MLP'
--AND ST.name not like 'tPlant'
OPEN UpdatePlantId_Crsr
IF ##CURSOR_ROWS > 0
BEGIN
FETCH NEXT FROM UpdatePlantId_Crsr INTO #TableName,#ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Sql= N''
SELECT #Sql = #Sql + N' UPDATE '+#TableName +' SET '+#ColumnName+ '= '+ #Value +'
'
PRINT #Sql
--EXEC(#Sql)
FETCH NEXT FROM UpdatePlantId_Crsr INTO #TableName,#ColumnName
END
END
CLOSE UpdatePlantId_Crsr
DEALLOCATE UpdatePlantId_Crsr
I suspect that something is wrong with your application if you need to use dynamic SQL to set tables and columns to NULL. That said, the question can still be answered.
You should use sp_executesql. But given that the table and column names cannot be passed in, just set up the SQL correctly:
SET #Sql = N'UPDATE ' + #TableName + ' SET ' + #ColumnName + ' = NULL' ;
EXEC sp_executesql #sql; -- yuo can still use it with no parameters
I am not sure why you are concatenating the #sql string, so I removed that.
Note that + NULL returns NULL -- both for string concatenation and addition.

In dynamic SQL, table is not being created

I have to use the configurations table to create a table if the table_status is new, and I have written the code but the table 'Dimcampaign' is not being created.
Initially you set #Tablestatus to a SQL statement, but the while loop requires #Tablestatus = 'New'. You should change how you initialize your #Tablestatus variable so that the logic flows into the while loop.
I would guess that you actually want #Tablestatus to be initialized to the value in your dbo.configuration table. For example:
declare #Tablestatus varchar(MAX)
select #Tablestatus = tablestatus from dbo.configuration
That being said, your while loop does not have a way to terminate. For example, inside the loop you should reset #Tablestatus so that it can become something other than 'New' so the loop can end.
For example:
while #Tablestatus = 'New' begin
-- maybe do some stuff here
update dbo.configuration set tablestatus = 'Old'
-- maybe do some other stuff here
select #Tablestatus = tablestatus from dbo.configuration
end
Looking a little more at your post, I don't really understand why you have a while loop there. It looks like you want something like this:
-- Initialize variables with values from dbo.configuration
declare #Tablestatus varchar(MAX)
select #Tablestatus = tablestatus from dbo.configuration
declare #If_New_Table_then_PrimaryKey NVARCHAR(MAX)
select #If_New_Table_then_PrimaryKey = If_New_Table_then_PrimaryKey from dbo.configuration
declare #NewColumn varchar(max)
select #NewColumn = Newcolumn from dbo.configuration
declare #Datatypes NVARCHAR(MAX)
select #Datatypes = Datatypes from dbo.configuration
declare #sql varchar(100)
-- Now that your variables are initialized you
-- can use the variables to take the appropriate action
if #Tablestatus = 'New' and #If_New_Table_then_PrimaryKey = 'Yes'
begin
set #sql = 'Create table' + #tablename + '(' + #Newcolumn + #Datatypes + ' PRIMARY KEY' + '(' + #Newcolumn + ')' + ');'
print (#sql)
exec(#sql)
end
if #Tablestatus = 'Old'
begin
set #sql = 'Alter table' + #tablename + 'ADD' + #Newcolumn + #Datatypes
print (#sql)
exec(#sql)
end
I like playing around with cursors and dynamic SQL. Something like this should do the trick, I'd add a whole lot of checking though! What if tablename contains spaces (you might like to use QUOTENAME), or what if datatypes doesn't contain a valid datatype? Etc etc, I can think of 100 things that can go wrong. Generally, when you want to do this, you need to rethink your design. But I have been in situations where I had to do something similar.
DECLARE #Tablestatus NVARCHAR(4000)
, #tablename NVARCHAR(4000)
, #If_New_Table_then_PrimaryKey NVARCHAR(4000)
, #NewColumn NVARCHAR(4000)
, #Datatypes NVARCHAR(4000)
, #sql NVARCHAR(4000);
DECLARE cur CURSOR LOCAL FAST_FORWARD FOR
SELECT tablestatus
, tablename
, If_New_Table_then_PrimaryKey
, Newcolumn
, Datatypes
FROM dbo.configuration;
OPEN cur;
FETCH NEXT FROM cur
INTO #Tablestatus
, #tablename
, #If_New_Table_then_PrimaryKey
, #NewColumn
, #Datatypes;
WHILE ##FETCH_STATUS = 0
BEGIN;
IF #Tablestatus = 'New'
AND #If_New_Table_then_PrimaryKey = 'Yes'
BEGIN;
SET #sql = N'Create table ' + #tablename + N'(' + #NewColumn + N' ' + #Datatypes + N' PRIMARY KEY' + N'(' + #NewColumn + N')' + N');';
PRINT ( #sql );
EXEC sys.sp_executesql #sql;
END
IF #Tablestatus = 'Old'
BEGIN;
SET #sql = N'Alter table ' + #tablename + N'ADD ' + #NewColumn + N' ' +#Datatypes;
PRINT ( #sql );
EXEC sys.sp_executesql #sql;
END;
FETCH NEXT FROM cur
INTO #Tablestatus
, #tablename
, #If_New_Table_then_PrimaryKey
, #NewColumn
, #Datatypes
END;
CLOSE cur;
DEALLOCATE cur;

Select from list of tables / multiple tables with nested query - MS SQL

Not sure if this has an easy answer, but I'm basically trying to do this:
select id
from (select table_name from information_schema.columns where column_name = 'id')
where id = 1234
So passing in a list of tables into the FROM.
I'm trying to check all tables where this column exists if they have a matching value for 1234.
And without typing 31 times, select from table1, select from table2, etc etc
And without doing a bunch of joins.
possible?
I wrote this stored procedure.
It SUCKS I know, I've never written anything in tsql (teesquill?) before, and my goal was just to get something that works.
// find all occurrences of value in DB
USE [your db here]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROC [dbo].[usp_query] (
#value NVARCHAR(128),
#colName NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #sql NVARCHAR(MAX);
-- DECLARE #tables NVARCHAR(MAX);
DECLARE #tabName NVARCHAR(MAX);
-- DECLARE #colName NVARCHAR(MAX);
DECLARE #count INT;
-- construct SQL
--SET #tables = N'SELECT Table_name FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME LIKE "yourcollumn"'
DECLARE tabs CURSOR FOR SELECT table_name FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME LIKE 'yourcollumn'
OPEN tabs
FETCH NEXT FROM tabs INTO #tabName
WHILE ##fetch_status = 0
BEGIN
--SET #count = N'SELECT count ( ' + #colName + ' ) FROM ' + #tabName + ' where ' + #colName + ' = ' + #value
DECLARE #SQLString NVARCHAR(500);
DECLARE #ParmDefinition NVARCHAR(500);
DECLARE #varOut NVARCHAR(25);
SET #SQLString = N'SELECT #count = count ( ' + #colName + ' ) FROM ' + #tabName + ' where ' + #colName + ' = ' + #value;
SET #ParmDefinition = N'#count NVARCHAR(25) OUTPUT';
EXECUTE sp_executesql #SQLString
,#ParmDefinition
,#count = #varOut OUTPUT;
SET #sql = N'SELECT ' + #colName + ' FROM ' + #tabName + ' where yourcollumn = ' + #value;
IF #varOut > 0
SELECT #tabName AS 'value found in table'
--EXEC sp_executesql #sql
FETCH NEXT FROM tabs INTO #tabName
END
CLOSE tabs
END;

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'

SQL Cursor within Stored Procedure to populate string variable

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