SQL Server - NTEXT columns and string manipulation I have a table with an NTEXT column called comments. I have a second string, let's call it anothercomment (a varchar) that needs placing inside a given comments string after the word UPDATEHERE.

Casting to nvarchar(max) truncates the comments string, so I cannot use the likes of CHARINDEX() (Msg 8152, Level 16, State 10, Line 2
String or binary data would be truncated.). I have used datalength() to check that there are a few thousand columns that are >8000 characters. 

An example of what I want to achieve (albeit with much longer strings):


  comments - This is a test UPDATEHERE This is the end of the test
  
  anothercomment - . This is inserted.
  
  Resulting string -  This is a test UPDATEHERE. This is inserted.  This is the end of the test


I realize that this is trivial with a normal varchar()/nvarchar(), but ntext is a complete and utter nightmare to work with. I realize it's a deprecated data type, but I did not write the application in question.
Converting to nvarchar(max) and back to ntext does make life simpler from a code point of view, but it does mean converting and rewriting the whole (perhaps very large) value, with all the CPU and logging overhead that implies.

An alternative is to use UPDATETEXT. This is deprecated, just like ntext, but it can reduce the logging overhead significantly. On the downside, it means using text pointers, and it only operates on one row at a time.

The following example code uses a cursor to work around that limitation, and uses PATINDEX instead of CHARINDEX since the former is one of the few functions that work directly with ntext:

Sample data

CREATE TABLE dbo.PhilsTable
(
    comment ntext NULL,
    anothercomment nvarchar(50) NULL
);

INSERT dbo.PhilsTable
    (comment, anothercomment)
VALUES 
(
    CONVERT(ntext, 
        N'This is a test UPDATEHERE This is the end of the test ' + 
            REPLICATE (CONVERT(nvarchar(max), N'x'), 1000000)), 
    CONVERT(nvarchar(50), N'. This is inserted.')
),
(
    CONVERT(ntext, 
        N'This is a test UPDATEHERE This is the end of the test ' + 
            REPLICATE (CONVERT(nvarchar(max), N'x'), 1000000)), 
    CONVERT(nvarchar(50), N'. This is inserted.')
),
(
    CONVERT(ntext, 
        N'This is a test UPDATEHERE This is the end of the test ' + 
            REPLICATE (CONVERT(nvarchar(max), N'x'), 1000000)), 
    CONVERT(nvarchar(50), N'. This is inserted.')
);


Cursor declaration

DECLARE c 
    CURSOR GLOBAL 
    FORWARD_ONLY 
    DYNAMIC 
    SCROLL_LOCKS 
    TYPE_WARNING
FOR
SELECT
    TxtPtr = TEXTPTR(PT.comment),
    Src = PT.anothercomment,
    Offset = PATINDEX(N'%UPDATEHERE%', PT.comment) + LEN(N'UPDATEHERE') - 1
FROM dbo.PhilsTable AS PT
WHERE
    PT.comment LIKE N'%UPDATEHERE%'; -- LIKE works with ntext

OPEN c;


Processing loop

DECLARE 
    @Ptr binary(16),
    @Src nvarchar(50),
    @Offset integer;

SET STATISTICS XML OFF; -- No cursor fetch plans

BEGIN TRANSACTION;

    WHILE 1 = 1
    BEGIN
        FETCH c INTO @Ptr, @Src, @Offset;

        IF @@FETCH_STATUS = -2 CONTINUE; -- row missing
        IF @@FETCH_STATUS = -1 BREAK; -- no more rows

        IF 1 = TEXTVALID('dbo.PhilsTable.comment', @Ptr)
        BEGIN
            -- Modify ntext value
            UPDATETEXT dbo.PhilsTable.comment @Ptr @Offset 0 @Src;
        END;
    END;

COMMIT TRANSACTION;

CLOSE c; DEALLOCATE c;