I am in the midst of updating data in multiple tables. Currently I have a table that has one field, "sources", that is just a list of all tables that include the field "itemid". I also have a table that has 2 fields, "itemid" and "olditemid". In TSQL, I would like to iterate through the sources and create the update statements on the fly. Here is what I was trying to do but I get some errors in the update statement that my variable is not declared. I am not sure this is even close the correct way I should be doing this. Ideas?
DECLARE #tblName varchar(50)
DECLARE process_cursor CURSOR FOR
SELECT source
FROM tmpTableNames
OPEN process_cursor
FETCH NEXT FROM processcursor
INTO #tblName
WHILE ##FETCH_STATUS = 0
UPDATE #tblName
SET itemid = r.itemid
FROM #tblName v, itemref r
WHERE r.olditemid = v.itemid
FETCH NEXT FROM process_cursor
INTO #tblName
END
CLOSE processcursor
DEALLOCATE processcursor
What you are trying to do is referred to as "dynamic SQL". While you're on the right track, you can't simply stick a variable in place of an object name and execute the query. I'll leave the pitfalls of dynamic SQL to someone else. What you're looking for is this:
DECLARE #tblName varchar(50)
DECLARE process_cursor CURSOR FOR
SELECT source
FROM tmpTableNames
OPEN process_cursor
FETCH NEXT FROM processcursor
INTO #tblName
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #sql NVARCHAR(500)
SELECT #sql = 'UPDATE [' + #tbleName + '] SET itemid = r.itemid FROM [' + #tbleName + '] v, itemref r WHERE r.ilditemid = v.itemid'
EXEC sp_executesql #sql
FETCH NEXT FROM process_cursor
INTO #tblName
END
CLOSE processcursor
DEALLOCATE processcursor
What this does is turn your update query into a string, then passes the SQL contained in that string to the sp_executesql stored procedure (this is the recommended way of executing dynamic sql, rather than EXEC('foo')).
I don't think you can do it using a variable like that. You could use dynamic SQL for the update:
DECLARE #sql VARCHAR(1000)
SET #sql = 'UPDATE' + #tableName + etc..
EXEC ( #sql )
And just do this inside your cursor.
You can't execute sql dynamically like this - you need to pass a dynamically generated string into the exec function like this:
DECLARE #tblName varchar(50)
DECLARE process_cursor CURSOR FOR
SELECT source
FROM tmpTableNames
OPEN process_cursor
FETCH NEXT FROM processcursor
INTO #tblName
WHILE ##FETCH_STATUS = 0
Declare #sql varchar(5000)
Select #sql = 'UPDATE ' + #tblName +
'SET itemid = r.itemid
FROM ' + #tblName + ' v, itemref r
WHERE r.olditemid = v.itemid'
Exec #sql
FETCH NEXT FROM process_cursor
INTO #tblName
END
CLOSE processcursor
DEALLOCATE processcursor
did you try
DECLARE #tblName varchar(50)?
I would think that would do it.
I've never been successful with variable-based UPDATE statements (i.e., UPDATE #tblName), unless I captured them into a string and executed these dynamically, as in:
EXEC 'UPDATE ' + #tblName + '
SET ItemId = (SELECT r.ItemId FROM itemref r WHERE r.OldItemId = ' + #tblName + '.itemId)'
For table TheTable, this should expand to:
EXEC 'UPDATE TheTable
SET ItemId = (SELECT r.ItemId FROM itemref r WHERE r.OldItemId = TheTable.ItemId)'
Related
I'm very close to gaining understanding how cursors work and using them for small tasks.
i get the following error.
An aggregate may not appear in the set list of an UPDATE statement
I'm guessing my issue here is the UPDATE and the SELECT stamement using an aggregate function such as MAX, but i'm finding it difficult to re-work my quoted statement here. from other posts i've read, its said to use a sub-query?
Base table is built using..
SELECT
QUOTENAME(SCHEMA_NAME(sOBJ.schema_id)) AS [DB_Schema],
QUOTENAME(sOBJ.name) AS [TableName],
SUM(sPTN.Rows) AS [Row_Count]
INTO ##tmpRowCount2
FROM
sys.objects AS sOBJ
INNER JOIN sys.partitions AS sPTN
ON sOBJ.object_id = sPTN.object_id
WHERE
sOBJ.type = 'U'
AND sOBJ.is_ms_shipped = 0x0
AND index_id < 2
GROUP BY
sOBJ.schema_id
, sOBJ.name
ORDER BY [Row_Count]
GO
ALTER TABLE ##tmpRowCount2 ADD updated_timestamp datetime NULL;
DECLARE #Row_Count int
DECLARE #sql nvarchar(max)
DECLARE #TableName as VARCHAR(256)
DECLARE #DB_Schema as VARCHAR(256)
DECLARE #updated_timestamp as DATETIME
DECLARE tablenamefromcursor CURSOR FOR
SELECT TableName, Row_Count, DB_Schema
FROM ##tmpRowCount2
OPEN tablenamefromcursor
FETCH NEXT FROM tablenamefromcursor INTO #TableName, #Row_Count, #DB_Schema
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'UPDATE ##tmpRowCount2 SET updated_timestamp = ' +
'(SELECT MAX([updated_timestamp]) FROM ' + #DB_Schema + '.' + #TableName +
') WHERE TableName = ''' + #TableName + ''''
select #sql
EXEC(#sql)
FETCH NEXT FROM tablenamefromcursor INTO #TableName, #Row_Count, #DB_Schema
END
CLOSE tablenamefromcursor
DEALLOCATE tablenamefromcursor
i'm now getting the following errors
Code editied above and i can confirm it works, by debugging with select #sql iwas able to parse the statement and edit the syntax unitl it worked by getting the parethesis and quotes in the right order
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
I have a collection (100+) of tables all of which contain two fields
RowChanged bit
ChangedFields bit
Now, an error has occurred which leave some entries with RowChanged = 1 while ChangedFields is empty. I therefore need to go through these and set RowChanged = 0 where ChangedFields empty.
I have achieved this by the following cursor.
BEGIN TRANSACTION
USE DatabaseName --Database name to clean
DECLARE #Table_Name VARCHAR(50)
DECLARE #Query VARCHAR(250)
DECLARE Table_Cursor CURSOR FOR SELECT Name FROM sys.tables;
DECLARE #Affected_Rows INTEGER = 0
OPEN Table_Cursor
FETCH NEXT FROM Table_Cursor INTO #Table_Name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Query = 'Update '+#Table_Name+' Set RowChanged = 0 Where RowChanged = 1 And (LEN(RTRIM(CONVERT(NVARCHAR(100), ChangedFields))) = 0 OR ChangedFields IS NULL)'
EXEC (#Query)
SET #Affected_Rows = #Affected_Rows + COALESCE(##ROWCOUNT, 0)
FETCH NEXT FROM Table_Cursor INTO #Table_Name
END
SELECT #Affected_Rows AS Affected_Rows
CLOSE Table_Cursor
DEALLOCATE Table_Cursor
ROLLBACK --Change to COMMIT in order to save changes
While this does work, I have a genetic aversion against using cursors. Also I have just learned that Apply can in many cases achieve what Cursors did pre-2005.
What I need to do is to go though all tables in the database and check for the condition where RowChanged = 1 and ChangedFields like '' or NULL.
I have tried working this out with TVFs and what not, but I keep coming up short. While I could do operations in a single table, getting the list from sys.tables and doing something on several tables have eluded me.
You can replace the cursor with while loop statement. Try using statement like the following to achive the desired o/p.
select name into #table from sys.tables
while (select count(*) from #table)>0
begin
set rowcount 1
select #Table_Name = name from sys.tables
set rowcount 0
.......
.......
delete from #table where name = #Table_Name
end
The above snippet is from sybase, you might need to make minor modifications for the syntax. I hope that helps.
declare #stmt nvarchar(max)
select
#stmt =
isnull(#stmt + nchar(13) + nchar(10), '') +
'update '+ name +' set RowChanged = 0 Where RowChanged = 1 And (LEN(RTRIM(CONVERT(NVARCHAR(100), ChangedFields))) = 0 OR ChangedFields IS NULL)'
from sys.tables
print #stmt
sp_executesql #stmt = #stmt
there's also the undocumented stored procedure sp_MSforeachtable - SQL Server sp_msforeachtable usage to select only those tables which meet some condition. Actually, I've never used it in my work, so it's just for information.
I have one outer cursor and one inner cursor also have two tables to work with. Now with the outer cursor i'm making new columns in the table 1 and naming them by the values from the table two, and that works just fine. Problem is with the inner cursor witch i used to insert the values into those new columns from one specific column from another table. This seams not to work, but what confusing me is that i do not get any error messages. Now i hope you understand what i'm trying to do, here is the code so comment for more description about the problem :
DECLARE #rbr_param nvarchar(255)
DECLARE #vrednost nvarchar(255)
DECLARE #cName nvarchar(255)
DECLARE #sql nvarchar (255)
DECLARE curs CURSOR FOR SELECT DISTINCT rbr_param FROM dbo.parametri_pomocna ORDER BY rbr_param
OPEN curs
FETCH NEXT FROM curs
INTO #rbr_param
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cName = 'P_'+#rbr_param+'_P'
EXEC('ALTER TABLE dbo.Parametri ADD ' + #cName + ' nvarchar(255)')
DECLARE vrd CURSOR FOR SELECT DISTINCT vrednost FROM dbo.parametri_pomocna
OPEN vrd
FETCH NEXT FROM vrd
INTO #vrednost
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'INSERT INTO dbo.Parametri'+(#cName)+ ' SELECT vrednost FROM dbo.parametri_pomocna WHERE vrednost = '+#vrednost+ ' AND rbr_param = '+#rbr_param
if exists (select * from INFORMATION_SCHEMA.COLUMNS where table_name = 'dbo.Parametri' and column_name = '#cName')
begin
exec(#sql)
end
FETCH NEXT FROM vrd
INTO #vrednost
END --end vrd
CLOSE vrd
DEALLOCATE vrd
FETCH NEXT FROM curs
INTO #rbr_param
END
CLOSE curs
DEALLOCATE curs
You have two problems here:
if exists ( select * from INFORMATION_SCHEMA.COLUMNS
where table_name = 'dbo.Parametri'
and column_name = '#cName'
)
(1) This view will never have table_name = schema name and table name.
(2) You have enclosed your variable name in single quotes for some reason.
For both of these reasons, your IF condition will never return true.
Try:
IF EXISTS
(
SELECT 1 FROM sys.columns
WHERE [object_id] = OBJECT_ID('dbo.Parametri')
AND name = #cName
)
(And here is why I prefer catalog views over INFORMATION_SCHEMA.)
Also this double-nested cursor thing seems quite inefficient and a lot more code than necessary to achieve what I think you're trying to do. How about something like this instead:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + N'ALTER TABLE dbo.Parametri ADD '
+ QUOTENAME('P_' + rbr_param + '_P') + ' NVARCHAR(255);'
FROM dbo.parametri_pomocna GROUP BY rbr_param;
EXEC sp_executesql #sql;
SET #sql = N'';
SELECT #sql = #sql + N'INSERT dbo.Parametri('+QUOTENAME('P_' + rbr_param + '_P')+ ')
SELECT vrednost
FROM dbo.parametri_pomocna WHERE rbr_param = ''' + rbr_param + '''
GROUP BY vrednost;'
FROM dbo.parametri_pomocna
GROUP BY rbr_param;
EXEC sp_executesql #sql;
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'