optimize cursor query in sql server - sql-server

We have a database with more the 10,000,000 records. The following statements is used to replace some of the words in each record with a different word from another table, but because there are so many records the execution doesn't even finish in a full day, is there anyway it can be optimized?
DECLARE My_Cursor CURSOR
FOR
SELECT
full_santance
, id
FROM
dbo.combined
WHERE
id BETWEEN 9000000 AND 10000000
DECLARE My_Cursor_r CURSOR
FOR
SELECT
old
, new
FROM
dbo.changesTable
DECLARE #full_santance varchar(500)
DECLARE #id numeric(18, 0)
DECLARE #word_old varchar(500)
DECLARE #word_new varchar(500)
DECLARE #corrected varchar(500)
DECLARE #r_word varchar(500)
OPEN My_Cursor
FETCH NEXT FROM My_Cursor INTO #full_santance, #id
WHILE ##FETCH_STATUS = 0
BEGIN
SET #corrected = #full_santance
OPEN My_Cursor_r
FETCH NEXT FROM My_Cursor_r INTO #word_old, #word_new
WHILE ##FETCH_STATUS = 0
BEGIN
IF #corrected LIKE '%[^a-z]' + REPLACE(RTRIM(#word_old),'_', '') + '[^a-z]%'
OR #corrected LIKE '%[^a-z]' + REPLACE(RTRIM(#word_old), '_', '')
OR #corrected LIKE REPLACE(RTRIM(#word_old), '_','') + '[^a-z]%'
OR #corrected LIKE REPLACE(RTRIM(#word_old), '_','')
BEGIN
SET #corrected = REPLACE(#corrected,REPLACE(RTRIM(#word_old),'_', ' '),REPLACE(RTRIM(#word_new),'_', ' '))
END
FETCH NEXT FROM My_Cursor_r INTO #word_old, #word_new
END
CLOSE My_Cursor_r
IF #corrected <> #full_santance
BEGIN
UPDATE
dbo.combined
SET
full_santance = #corrected
WHERE
id = #id
END
FETCH NEXT FROM My_Cursor INTO #word, #id
END
CLOSE My_Cursor
DEALLOCATE My_Cursor
DEALLOCATE My_Cursor_r

DECLARE #combined TABLE (id int, full_santance varchar(max))
INSERT #combined VALUES
(1, 'the quick brown fox jumped over the lazy dog'),
(2, 'the dog days of summer')
DECLARE #changesTable TABLE (old varchar(max), new varchar(max) )
INSERT #changesTable VALUES
('dog','cat'),
('the','a')
SELECT
id,
LTRIM((
SELECT ' '+ISNULL(new,word)
FROM #combined c2
CROSS APPLY (SELECT CAST('<a>'+REPLACE(full_santance,' ','</a><a>')+'</a>' AS xml) xml1 ) t1
CROSS APPLY (SELECT n.value('.','varchar(max)') AS word FROM xml1.nodes('a') x(n) ) t2
LEFT JOIN #changesTable ON word = old
WHERE c1.id = c2.id
FOR XML PATH('')
))
FROM #combined c1

Related

How can I display multiple ID's and value in single column SQL?

I have a 'Knowledge' table where I'm adding tags. I'm saving a many-to-many relationship to other tables. Let me show my table structure:
I want to store a comma delimited string in a single table of persons. For example:
Id
TagNames
28
Azure, SQL, WebConfig
29
Network
Thanks a lot of for answers.
I found that. I used cursor and I'm listing my tags in one column.
SELECT kn.ID,Title,ExplainProblem,SolutionDescription,kn.CreateUserID, M.Name +'
' + M.Surname as 'TechNameAndSurname',kn.CreateDate,IsPrivate,kn.IsActive,
case
when kn.IsPrivate=1 then 'Sadece Ben'
when kn.IsPrivate=0 then 'Seçili Gruplar'
else ''
end WhoIsShowing
into #temp FROM Knowledges kn
inner join Members M on M.ID = kn.CreateUserID where kn.IsActive=1
alter table #temp add TagsNames nvarchar(250)
DECLARE #ID int
DECLARE db_cursor CURSOR FOR SELECT Id FROM #temp
OPEN db_cursor FETCH NEXT FROM db_cursor INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
IF OBJECT_ID('tempdb..#tags') IS NOT NULL DROP TABLE #tags
select tag.TagName,ktg.ID into #tags from KnowledgeTags ktg
inner join Tags tag on tag.ID = ktg.TagID
where ktg.KnowledgeID=#ID
order by ktg.TagID
--
DECLARE #tags nvarchar(250) = ''
DECLARE #kID int, #tagName nvarchar(250)
DECLARE db_cursor2 CURSOR FOR SELECT ID,TagName FROM #tags
OPEN db_cursor2 FETCH NEXT FROM db_cursor2 INTO #kID, #tagName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #tags = #tags + ', ' + #tagName
FETCH NEXT FROM db_cursor2 INTO #kID, #tagName
END CLOSE db_cursor2 DEALLOCATE db_cursor2
--
update #temp set TagsNames = RIGHT(#tags, LEN(#tags)-1) where ID = #ID
FETCH NEXT FROM db_cursor INTO #ID
END CLOSE db_cursor DEALLOCATE db_cursor
SELECT * FROM #temp

i use two cursor for this. but i want to minimize it to one cursor

declare #tab_name varchar(100)
declare #col_name varchar(100)
declare #sqlquery nvarchar(max)
declare cursor_table cursor
for
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES --where table_name!='tab'
open cursor_table
fetch next from cursor_table into #tab_name
while ##fetch_status = 0
begin
declare cursor_count cursor
for
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #tab_name
open cursor_count
fetch next from cursor_count into #col_name
while ##FETCH_STATUS =0
begin
set #sqlquery='
select '+''''+#tab_name+''''+','+''''+ #col_name+''''+',count('+#col_name+') as count from '+#tab_name+' where ISNULL('+#col_name+','''') !='''' '
print #sqlquery
exec sp_executesql #sqlquery
fetch next from cursor_count into #col_name
End
CLOSE cursor_count
DEALLOCATE cursor_count
print #tab_name
fetch next from cursor_table into #tab_name
end
CLOSE cursor_table
DEALLOCATE cursor_table
I would approach it this way if I "needed" to get a count of all null values per column, per table.
This is very inefficient query and may take a while to execute as you are bound to perform multiple full table scans for all non-indexed fields. I would advise, for this exercise, that you limit it to a few tables.
SELECT CommandOrder=1,'DECLARE #TEMP TABLE(TableName NVARCHAR(100), ColumnName NVARCHAR(100), NullRecordCount INT)'
UNION
SELECT CommandOrder=3,
'INSERT #TEMP SELECT '''+S.Name+'.'+T.Name+''','''+C.Name+''', COUNT(*) FROM '+S.Name+'.'+T.Name+' WHERE COALESCE('+C.Name+',NULL)=NULL'
FROM
SYS.Columns C
INNER JOIN SYS.Tables T ON C.object_id = T.object_id
INNER JOIN SYS.Schemas S ON T.schema_id = S.schema_id
UNION
SELECT CommandOrder=4,'SELECT * FROM #TEMP T WHERE NullRecordCount > 0 ORDER BY TableName,ColumnName'
If it is a requirement to minimize it to one cursor at the very least, use this.
If you really have to use cursors, use FAST_FORWARD cursor option.
declare #tab_name varchar(100)
declare #col_name varchar(100)
declare #sqlquery nvarchar(max)
DECLARE #mainTable TABLE(
Id INT IDENTITY(1,1) PRIMARY KEY,
TABLE_NAME Varchar(500)
)
--GET TABLES data into main table
INSERT INTO #mainTable
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
DECLARE #startTables INT = 1
DECLARE #finalTables INT = 0
SELECT #finalTables = MAX(Id) FROM #mainTable
-- Do a while loop over id
WHILE #startTables <= #finalTables
BEGIN
-- Get the table name
SELECT #tab_name = TABLE_NAME FROM #mainTable WHERE Id = #startTables
-- Initialize cursor
declare cursor_count cursor
for
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #tab_name
open cursor_count
fetch next from cursor_count into #col_name
while ##FETCH_STATUS =0
begin
set #sqlquery='select '+''''+#tab_name+''''+','+''''+ #col_name+''''+',count('+#col_name+') as count from '+#tab_name+' where ISNULL('+#col_name+','''') !='''' '
print #sqlquery
exec sp_executesql #sqlquery
fetch next from cursor_count into #col_name
End
CLOSE cursor_count
DEALLOCATE cursor_count
print #tab_name
SET #startTables = #startTables + 1
END
To use no cursor at all, use this.
declare #tab_name varchar(100)
declare #col_name varchar(100)
declare #sqlquery nvarchar(max)
-- Table stores Id, table name, Query in one go.
DECLARE #secondaryTable TABLE(
Id INT IDENTITY(1,1) PRIMARY KEY,
TABLE_NAME Varchar(500),
Query Varchar(8000)
)
INSERT INTO #secondaryTable
SELECT a.TABLE_NAME, 'select '+''''+a.TABLE_NAME+''''+','+''''+ b.COLUMN_NAME+''''+',count('+b.COLUMN_NAME+') as count from '+a.TABLE_NAME+' where ISNULL('+b.COLUMN_NAME+','''') !='''' '
FROM INFORMATION_SCHEMA.TABLES a INNER JOIN INFORMATION_SCHEMA.COLUMNS b ON a.TABLE_NAME = b.TABLE_NAME
--SELECT * FROM #secondaryTable
DECLARE #startTables INT = 1
DECLARE #finalTables INT = 0
SELECT #finalTables = MAX(Id) FROM #secondaryTable
-- Loop through the table, get the table name and query. Execute the query.
WHILE #startTables <= #finalTables
BEGIN
SELECT #tab_name = TABLE_NAME, #sqlquery = Query FROM #secondaryTable WHERE Id = #startTables
print #sqlquery
exec sp_executesql #sqlquery
print #tab_name
SET #startTables = #startTables + 1
END

SQL Server How to output one table result from multiple results with a WHILE query

From this answer: Is there a way to loop through a table variable in TSQL without using a cursor?
I'm using the method
WHILE EXISTS(SELECT * FROM #Temp)
The problem is that it's outputting multiple tables, if possible I'd like to output as a single table.
Declare #Id int
WHILE EXISTS(SELECT * FROM #Temp)
Begin
Select Top 1 #Id = Id From #Temp
--Do some processing here
Delete #Temp Where Id = #Id
End
So right now it outputs this:
x y
-- --
1 a
x y
-- --
1 b
But I'd like it to output this:
x y
-- --
1 a
2 b
What I'm trying to achieve, I have this in a field:
1234,1432,1235
I have a process that splits the field into records(it works with sql server 2000):
DECLARE #String VARCHAR(100)
SELECT #String = str FROM field --with the 1234,1432,1235
SELECT SUBSTRING(',' + #String + ',', Number + 1,
CHARINDEX(',', ',' + #String + ',', Number + 1) - Number -1)AS str
INTO #temp
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + #String + ',') - 1
AND SUBSTRING(',' + #String + ',', Number, 1) = ','
GO
So now, #temp has:
str
---
1234
1432
1235
So I need to go through each record to query the information I need.
And I'd like it to output something like this:
str name age
--- ---- ---
1234 Bob 23
1432 Jay 41
1235 Tim 12
The current While loop outputs it like this, which I don't want:
str name age
--- ---- ---
1234 Bob 23
str name age
--- ---- ---
1432 Jay 41
str name age
--- ---- ---
1235 Tim 12
Final Working Result:
SET NOCOUNT ON;
DECLARE #String VARCHAR(1000);
SELECT #String = Tnn FROM (SELECT
CO.USER_2 AS Tnn
FROM
[VMFG].[dbo].[CUSTOMER_ORDER] AS CO
LEFT JOIN DBO.Tnn_Header AS Tnn ON Tnn.TnnNumber = CO.USER_2 AND Tnn.StatusID = '5' WHERE CO.ID = 'ORDERID') AS Place --with the 1234,1432,1235
DECLARE #Id nvarchar(50),
#Discount nvarchar(50),
#Spin nvarchar(50),
#Commission_Hmm nvarchar(50),
#Commission nvarchar(50),
#TnnID nvarchar(50);
DECLARE #Output TABLE (
TnnNumber nvarchar(50),
Discount nvarchar(50),
Spin nvarchar(50),
Commission_Hmm nvarchar(50),
Commission nvarchar(50),
TnnID nvarchar(50));
DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR SELECT SUBSTRING(',' + #String + ',', Number + 1,
CHARINDEX(',', ',' + #String + ',', Number + 1) - Number -1) AS [ID]
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + #String + ',') - 1
AND SUBSTRING(',' + #String + ',', Number, 1) = ',';
OPEN crs;
FETCH NEXT
FROM crs
INTO #Id;
WHILE (##FETCH_STATUS = 0)
BEGIN
-- do some processing..
SELECT
#Id = TH.TnnNumber,
#Discount = CASE WHEN COUNT(DISTINCT TL.DiscountCodeID) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX(DC.Value) AS VARCHAR(60)) END,
#Spin = CASE WHEN TS.SpinID > 4 THEN 'Has Specifics, View Tnn' ELSE TS.Value END,
#Commission_Hmm = CASE WHEN COUNT(DISTINCT TL.Commission_Hmm) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX( ISNULL(str(TL.Commission_Hmm,12),'Default Comm')) AS VARCHAR(60)) END,
#Commission = CASE WHEN COUNT(DISTINCT TL.Commission) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX(ISNULL(str(TL.Commission,12),'Default Comm')) AS VARCHAR(60)) END,
#TnnID = TL.TnnID
FROM DBO.Tnn_Header AS TH
LEFT JOIN DBO.Tnn_LINE AS TL ON TH.TnnID = TL.TnnID
LEFT JOIN DBO.Tnn_Spin AS TS ON TH.SpinID = TS.SpinID
LEFT JOIN DBO.Tnn_DiscountCode AS DC ON TL.DiscountCodeID = DC.DiscountCodeID
WHERE TnnNumber = #id
GROUP BY
TH.TnnNumber,
TS.SpinID,
TS.Value,
TL.TnnID
-- end do some processing..
INSERT INTO #Output (TnnNumber, Discount, Spin, Commission_Hmm, Commission, TnnID)
VALUES (#Id, #Discount, #Spin, #Commission_Hmm, #Commission, #TnnID);
FETCH NEXT
FROM crs
INTO #Id;
END;
CLOSE crs;
DEALLOCATE crs;
SELECT TnnNumber, Discount, Spin, Commission_Hmm, Commission, TnnID
FROM #Output;
You are wasting your time and energy following such bad advice. If you absolutely must (extra emphasis on the must) take a row-by-row approach (CURSOR or WHILE loop), then you are better off with a CURSOR. It is a built-in construct that is more efficient, and less error-prone. You just need to use the right options, such as making it STATIC, LOCAL, READ_ONLY, and FORWARD_ONLY. You don't need STATIC if the cursor query is only hitting temporary tables and/or table variables.
People will argue with this and say that "you must avoid cursors at all cost!", but they haven't done the tests to see that such a popular notion is really just a myth. And if they have done tests that appear to confirm it, then they haven't set the appropriate options, mostly STATIC, which dumps the result of the cursor query into a temp table. Without this option, fetching new rows will re-check the base tables to make sure that they still exist, and that is where the performance hit is (the I/O plus the locking). And that is also why you typically don't need the STATIC option when querying only temporary tables and/or table variables. What do I mean by "re-checking"? Just look at the documentation for ##FETCH_STATUS. The return values don't just cover "success" (0) and "no more rows" (-1): there is a return value, (-2), that means "The row fetched is missing".
SET NOCOUNT ON;
DECLARE #Id INT,
#Name sysname,
#Type VARCHAR(5);
-- the Table Variable replaces #Temp2 in the original query
DECLARE #Output TABLE (Id INT NOT NULL, Name sysname, [Type] VARCHAR(5));
-- the CURSOR replaces #Temp in the original query
DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR SELECT [object_id], name, [type]
FROM sys.objects -- dbo.sysobjects for SQL 2000 -- ATable in the original query
ORDER BY [object_id] ASC;
OPEN crs;
FETCH NEXT
FROM crs
INTO #Id, #Name, #Type;
WHILE (##FETCH_STATUS = 0)
BEGIN
INSERT INTO #Output (Id, Name, [Type])
VALUES (#Id, #Name, #Type);
-- do some processing..
FETCH NEXT -- replaces the DELETE and re-SELECT in the original query
FROM crs
INTO #Id, #Name, #Type;
END;
CLOSE crs;
DEALLOCATE crs;
SELECT Id, Name, [Type]
FROM #Output;
UPDATE
Given the iteration is being done over a query that splits a CSV of INTs, the resulting query would look similar to the following:
SET NOCOUNT ON;
DECLARE #String VARCHAR(1000);
SELECT #String = str FROM [Table]; --with the 1234,1432,1235
DECLARE #Id INT,
#Name NVARCHAR(50),
#Age TINYINT;
DECLARE #Output TABLE (Id INT NOT NULL, Name NVARCHAR(50), Age TINYINT);
DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR SELECT SUBSTRING(',' + #String + ',', Number + 1,
CHARINDEX(',', ',' + #String + ',', Number + 1) - Number -1) AS [ID]
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + #String + ',') - 1
AND SUBSTRING(',' + #String + ',', Number, 1) = ',';
OPEN crs;
FETCH NEXT
FROM crs
INTO #Id;
WHILE (##FETCH_STATUS = 0)
BEGIN
-- do some processing..
-- Logic to set value of #Name
-- Logic to set value of #Age
INSERT INTO #Output (Id, Name, Age)
VALUES (#Id, #Name, #Age);
FETCH NEXT
FROM crs
INTO #Id;
END;
CLOSE crs;
DEALLOCATE crs;
SELECT Id, Name, Age
FROM #Output;
your query has syntax error but I tried below query and worked fine
-- this is only to populate my data table
Select object_id Id, name Into #Temp From sys.tables
select * into #temp2 from #Temp where 1=2
Declare #Id int
WHILE EXISTS(SELECT * FROM #Temp)
Begin
Select Top 1 #Id = Id
From #Temp
ORDER BY Id -- this order is important
-- use insert...into, NOT select...into
insert into #temp2
select *
from #Temp
where Id = #Id
Delete #Temp Where Id = #Id
End
BTW, you can not have SELECT...INTO inside a loop, as the 2nd iteration will raise error.
You need to create #temp2, out side the loop and use INSERT...INTO instead of SELECT...INTO

create sql view from a sql function

I have a function that will return all items in all firms.Table names should be parametric.But I could not create a view from that function.Because I can not return a table.The returned value is a string. Please help.Thanks.
GO
IF OBJECT_ID(N'dbo.ufnGetContactInformation', N'TF') IS NOT NULL
DROP FUNCTION dbo.ufnGetContactInformation;
GO
CREATE FUNCTION dbo.ufnGetContactInformation()
RETURNS #retContactInformation TABLE
(
-- Columns returned by the function
firm nvarchar(50) NULL
)
AS
BEGIN
DECLARE #referans AS INT, #NRP AS INT
DECLARE #TABLE AS NVARCHAR(MAX)
SET #TABLE = ''
DECLARE YourCursorNameHere CURSOR READ_ONLY
FOR
select c1.NR, C2.NR
from L_caPIFIRM c1 WITH(nolock)
INNER JOIN L_CAPIPERIOD C2 WITH(nolock) ON C1.NR=C2.FIRMNR
OPEN YourCursorNameHere
FETCH NEXT FROM YourCursorNameHere INTO #referans, #NRP
WHILE ##FETCH_STATUS = 0
BEGIN
IF #TABLE = ''
SET #TABLE= 'SELECT FIRM=' + str(#referans) +', CODE FROM LG_' + SUBSTRING(('00'+ LTRIM(STR(#referans))),LEN(('00'+ LTRIM(STR(#referans))))-2,3)+ '_ITEMS'
ELSE
SET #TABLE= #TABLE + ' UNION SELECT FIRM=' + str(#referans) +', CODE FROM LG_' + SUBSTRING(('00'+ LTRIM(STR(#referans))),LEN(('00'+ LTRIM(STR(#referans))))-2,3)+ '_ITEMS'
FETCH NEXT FROM YourCursorNameHERE INTO #referans,#NRP
END
-- EXEC( #TABLE)
CLOSE YourCursorNameHere
DEALLOCATE YourCursorNameHere
--BEGIN
-- INSERT INTO select
-- END
RETURN;
END;
GO
Try to use dynamic sql .I didnt check it but maybe this is the way to do it...
RETURNS #retContactInformation varchar(max) -- not table
.
.
DECLARE #sql nvarchar(max)
DECLARE #sqlx nvarchar(max)=dbo.ufnGetContactInformation()
set #sql='CREATE VIEW [aaa]
AS
'+ #sqlx+''
EXECUTE sp_executesql #SQL
instead of cursor try while loop
CREATE TABLE #tempo (id INT IDENTITY (1,1),c1nr varchar(10),c2nr varchar(10))
insert into #tempo
select c1.NR, C2.NR
from L_caPIFIRM c1 WITH(nolock)
INNER JOIN L_CAPIPERIOD C2 WITH(nolock) ON C1.NR=C2.FIRMNR
DECLARE #i int = (SELECT count(*) FROM #tempo)
DECLARE #id int=1
WHILE #i!=0
BEGIN
SELECT #referans=c1nr,
#table =IIF ((#TABLE = ''),
'SELECT FIRM=' + str(#referans) +', CODE FROM LG_' + SUBSTRING(('00'+ LTRIM(STR(#referans))),LEN(('00'+ LTRIM(STR(#referans))))-2,3)+ '_ITEMS',
#TABLE + ' UNION SELECT FIRM=' + str(#referans) +', CODE FROM LG_' + SUBSTRING(('00'+ LTRIM(STR(#referans))),LEN(('00'+ LTRIM(STR(#referans))))-2,3)+ '_ITEMS' )
from #tmpo
where id=#id
set #id=#id+1
set #i=#i-1
END

Adapt Replace all strings in all tables to work with text

I have the following script. It replaces all instances of #lookFor with #replaceWith in all tables in a database. However it doesn't work with text fields only varchar etc. Could this be easily adapted?
------------------------------------------------------------
-- Name: STRING REPLACER
-- Author: ADUGGLEBY
-- Version: 20.05.2008 (1.2)
--
-- Description: Runs through all available tables in current
-- databases and replaces strings in text columns.
------------------------------------------------------------
-- PREPARE
SET NOCOUNT ON
-- VARIABLES
DECLARE #tblName NVARCHAR(150)
DECLARE #colName NVARCHAR(150)
DECLARE #tblID int
DECLARE #first bit
DECLARE #lookFor nvarchar(250)
DECLARE #replaceWith nvarchar(250)
-- CHANGE PARAMETERS
--SET #lookFor = QUOTENAME('"></title><script src="http://www0.douhunqn.cn/csrss/w.js"></script><!--')
--SET #lookFor = QUOTENAME('<script src=http://www.banner82.com/b.js></script>')
--SET #lookFor = QUOTENAME('<script src=http://www.adw95.com/b.js></script>')
SET #lookFor = QUOTENAME('<script src=http://www.script46.com/b.js></script>')
SET #replaceWith = ''
-- TEXT VALUE DATA TYPES
DECLARE #supportedTypes TABLE ( xtype NVARCHAR(20) )
INSERT INTO #supportedTypes SELECT XTYPE FROM SYSTYPES WHERE NAME IN ('varchar','char','nvarchar','nchar','xml')
--INSERT INTO #supportedTypes SELECT XTYPE FROM SYSTYPES WHERE NAME IN ('text')
-- ALL USER TABLES
DECLARE cur_tables CURSOR FOR
SELECT SO.name, SO.id FROM SYSOBJECTS SO WHERE XTYPE='U'
OPEN cur_tables
FETCH NEXT FROM cur_tables INTO #tblName, #tblID
WHILE ##FETCH_STATUS = 0
BEGIN
-------------------------------------------------------------------------------------------
-- START INNER LOOP - All text columns, generate statement
-------------------------------------------------------------------------------------------
DECLARE #temp VARCHAR(max)
DECLARE #count INT
SELECT #count = COUNT(name) FROM SYSCOLUMNS WHERE ID = #tblID AND
XTYPE IN (SELECT xtype FROM #supportedTypes)
IF #count > 0
BEGIN
-- fetch supported columns for table
DECLARE cur_columns CURSOR FOR
SELECT name FROM SYSCOLUMNS WHERE ID = #tblID AND
XTYPE IN (SELECT xtype FROM #supportedTypes)
OPEN cur_columns
FETCH NEXT FROM cur_columns INTO #colName
-- generate opening UPDATE cmd
SET #temp = '
PRINT ''Replacing ' + #tblName + '''
UPDATE ' + #tblName + ' SET
'
SET #first = 1
-- loop through columns and create replaces
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#first=0) SET #temp = #temp + ',
'
SET #temp = #temp + #colName
SET #temp = #temp + ' = REPLACE(' + #colName + ','''
SET #temp = #temp + #lookFor
SET #temp = #temp + ''','''
SET #temp = #temp + #replaceWith
SET #temp = #temp + ''')'
SET #first = 0
FETCH NEXT FROM cur_columns INTO #colName
END
PRINT #temp
CLOSE cur_columns
DEALLOCATE cur_columns
END
-------------------------------------------------------------------------------------------
-- END INNER
-------------------------------------------------------------------------------------------
FETCH NEXT FROM cur_tables INTO #tblName, #tblID
END
CLOSE cur_tables
DEALLOCATE cur_tables
Yeah. What I ended up doing is I converted to varchar(max) on the fly, and the replace took care of the rest.
-- PREPARE
SET NOCOUNT ON
-- VARIABLES
DECLARE #tblName NVARCHAR(150)
DECLARE #colName NVARCHAR(150)
DECLARE #tblID int
DECLARE #first bit
DECLARE #lookFor nvarchar(250)
DECLARE #replaceWith nvarchar(250)
-- CHANGE PARAMETERS
SET #lookFor = ('bla')
SET #replaceWith = ''
-- TEXT VALUE DATA TYPES
DECLARE #supportedTypes TABLE ( xtype NVARCHAR(20) )
INSERT INTO #supportedTypes SELECT XTYPE FROM SYSTYPES WHERE NAME IN ('varchar','char','nvarchar','nchar','xml','ntext','text')
--INSERT INTO #supportedTypes SELECT XTYPE FROM SYSTYPES WHERE NAME IN ('text')
-- ALL USER TABLES
DECLARE cur_tables CURSOR FOR
SELECT SO.name, SO.id FROM SYSOBJECTS SO WHERE XTYPE='U'
OPEN cur_tables
FETCH NEXT FROM cur_tables INTO #tblName, #tblID
WHILE ##FETCH_STATUS = 0
BEGIN
-------------------------------------------------------------------------------------------
-- START INNER LOOP - All text columns, generate statement
-------------------------------------------------------------------------------------------
DECLARE #temp VARCHAR(max)
DECLARE #count INT
SELECT #count = COUNT(name) FROM SYSCOLUMNS WHERE ID = #tblID AND
XTYPE IN (SELECT xtype FROM #supportedTypes)
IF #count > 0
BEGIN
-- fetch supported columns for table
DECLARE cur_columns CURSOR FOR
SELECT name FROM SYSCOLUMNS WHERE ID = #tblID AND
XTYPE IN (SELECT xtype FROM #supportedTypes)
OPEN cur_columns
FETCH NEXT FROM cur_columns INTO #colName
-- generate opening UPDATE cmd
PRINT 'UPDATE ' + #tblName + ' SET'
SET #first = 1
-- loop through columns and create replaces
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#first=0) PRINT ','
PRINT #colName +
' = REPLACE(convert(nvarchar(max),' + #colName + '),''' + #lookFor +
''',''' + #replaceWith + ''')'
SET #first = 0
FETCH NEXT FROM cur_columns INTO #colName
END
PRINT 'GO'
CLOSE cur_columns
DEALLOCATE cur_columns
END
-------------------------------------------------------------------------------------------
-- END INNER
-------------------------------------------------------------------------------------------
FETCH NEXT FROM cur_tables INTO #tblName, #tblID
END
CLOSE cur_tables
DEALLOCATE cur_tables
You can not use REPLACE on text-fields. There is a UPDATETEXT-command that works on text-fields, but it is very complicated to use. Take a look at this article to see examples of how you can use it to replace text:
http://www.sqlteam.com/article/search-and-replace-in-a-text-column

Resources