0

What I need is to search for a string in a specific column (datatype: text) of a table and replace it with another text.

For example

Id          |          Text
-----------------------------
1                   this is test
2                   that is testosterone

If I chose to replace test with quiz, results should be

this is quiz
that is quizosterone

What I've tried so far?

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROC [dbo].[SearchAndReplace] 
(
     @FindString    NVARCHAR(100)
    ,@ReplaceString NVARCHAR(100)
)
AS
BEGIN
    SET NOCOUNT ON
SELECT CONTENT_ID as id, CONTENT_TEXT, textptr(CONTENT_TEXT) as ptr, datalength(CONTENT_TEXT) as lng
 INTO #newtable6  FROM HTML_CONTENTS 
    DECLARE @COUNTER INT = 0
    DECLARE @TextPointer VARBINARY(16) 
    DECLARE @DeleteLength INT 
    DECLARE @OffSet INT 

    SELECT @TextPointer = TEXTPTR(CONTENT_TEXT)
      FROM #newtable6

    SET @DeleteLength = LEN(@FindString) 
    SET @OffSet = 0
    SET @FindString = '%' + @FindString + '%'

    WHILE (SELECT COUNT(*)
             FROM #newtable6
            WHERE PATINDEX(@FindString, CONTENT_TEXT) <> 0) > 0
    BEGIN 
        SELECT @OffSet = PATINDEX(@FindString, CONTENT_TEXT) - 1
          FROM #newtable6
         WHERE PATINDEX(@FindString, CONTENT_TEXT) <> 0

UPDATETEXT #newtable6.CONTENT_TEXT
            @TextPointer
            @OffSet
            @DeleteLength
            @ReplaceString

           SET @COUNTER = @COUNTER + 1
    END
    select @COUNTER,* from #newtable6
drop table #newtable6
    SET NOCOUNT OFF

I get the error:

Msg 7116, Level 16, State 4, Procedure SearchAndReplace, Line 31
Offset 1900 is not in the range of available LOB data.
The statement has been terminated.

Thank you

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Pabuc
  • 5,528
  • 7
  • 37
  • 52

2 Answers2

5

If you can't change your column types permanently, you can cast them on the fly:

ALTER PROC [dbo].[SearchAndReplace] 
(@FindString    VARCHAR(100),
 @ReplaceString VARCHAR(100) )
AS
BEGIN
   UPDATE dbo.HTML_CONTENTS
   SET CONTENT_TEXT = cast (REPLACE(cast (CONTEXT_TEXT as varchar(max)), @FindString, @ReplaceString) as TEXT)
END
bernd_k
  • 11,558
  • 7
  • 45
  • 64
  • Wish I could accept 2 answers since your answer actually works with the existing table structure :) thank you – Pabuc Dec 30 '10 at 12:54
  • @Pabuc, but changing that structure should be a very high priority. What you have now will NOT work with the next version of SQL Server. – HLGEM Dec 31 '10 at 18:46
3

The datatype TEXT is deprecated and should not be used anymore - exactly because it's clunky and doesn't support all the usual string manipulation methods.

From the MSDN docs on text, ntext, image:

ntext, text, and image data types will be removed in a future version of MicrosoftSQL Server. Avoid using these data types in new development work, and plan to modify applications that currently use them. Use nvarchar(max), varchar(max), and varbinary(max) instead.

My recommendation: convert that column to VARCHAR(MAX) and you should be fine after that!

ALTER TABLE dbo.HTML_CONTENTS 
   ALTER COLUMN CONTEXT_TEXT VARCHAR(MAX)

That should do it.

When your column is VARCHAR(MAX), then your stored procedures becomes totally simple:

ALTER PROC [dbo].[SearchAndReplace] 
(@FindString    VARCHAR(100),
 @ReplaceString VARCHAR(100) )
AS
BEGIN
   UPDATE dbo.HTML_CONTENTS
   SET CONTENT_TEXT = REPLACE(CONTEXT_TEXT, @FindString, @ReplaceString)
END

Two observations on the side:

  • it would be helpful to have a WHERE clause in your stored proc, in order not to update the whole table (unless that's what you really need to do)

  • you're using TEXT in your table, yet your stored procedure parameters are of type NVARCHAR - try to stick to one set - either TEXT/VARCHAR(MAX) and regular VARCHAR(100) parameters, or then use all Unicode strings: NTEXT/NVARCHAR(MAX) and NVARCHAR(100). Constantly mixing those non-Unicode and Unicode strings is a mess and causes lots of conversions and unnecessary overhead

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • I can't convert it to varchar because the content is just too big for it. I know that replace is much more easier but this is what I have to do, I have to use UPDATETEXT for sure but thanks – Pabuc Dec 30 '10 at 12:31
  • 1
    @Pabuc: VARCHAR(MAX) can hold **up to 2 GByte** of text !! That's over 300 times the contents of Tolstoj's `War and Peace` - that **ought** to be enough even for your needs !! – marc_s Dec 30 '10 at 12:32
  • 1
    Ok, after reading, I learned that it is big enough for my data but still, can't change it. This is a working application and just need to change a 2 word string with another string for the config table. Don't want to change them manually so now here I am :) – Pabuc Dec 30 '10 at 12:34
  • Also marc, I want to update every row that has @FindString in it. For my example, it should work since it is in a while loop. Am I wrong? – Pabuc Dec 30 '10 at 12:41
  • @Pabuc: my recommendation: stop wasting your time trying to figure out how to do it with TEXT / text pointers and all those kludges - just **change** it and be happy ! – marc_s Dec 30 '10 at 12:41
  • Would it cause any other problems (reading/writing) or can I change it with no doubts? :) thanks again – Pabuc Dec 30 '10 at 12:42
  • 2
    @Pabuc: in my experience, I never saw any problems with changing away from TEXT. Of course, any stored proc that does this messy textpointer kludge needs to be changed - but all new versions tend to be **a lot** simpler and nicer to use. **OF COURSE:** try this on a **test** environment first! But I've never seen any problem, really - the new VARCHAR(MAX) behaves just like any other VARCHAR column, and supports **ALL** usual string functions - that's the way it should have been from the start, really.... – marc_s Dec 30 '10 at 12:44
  • 1
    And don't ignore the fact that text is deprecated. It will not be available in the next version of SQL server. Start changing everything now. – HLGEM Dec 31 '10 at 18:45