SQL Server: columnas NTEXT y manipulación de cadenas

11

Tengo una tabla con una NTEXTcolumna llamada comments. Tengo una segunda cadena, llamémosla anothercomment(a varchar) que necesita colocarse dentro de una commentscadena dada después de la palabra UPDATEHERE.

La conversión a nvarchar(max)trunca la commentscadena, por lo que no puedo usar los gustos de CHARINDEX()( Msg 8152, Level 16, State 10, Line 2 String or binary data would be truncated.). He utilizado datalength()para comprobar que hay unos pocos miles de columnas que son> 8000 caracteres.

Un ejemplo de lo que quiero lograr (aunque con cadenas mucho más largas):

comentarios This is a test UPDATEHERE This is the end of the test

otro comentario . This is inserted.

Cadena resultante This is a test UPDATEHERE. This is inserted. This is the end of the test

Me doy cuenta de que esto es trivial con un varchar()/ normal nvarchar(), pero ntextes una pesadilla completa y absoluta para trabajar. Me doy cuenta de que es un tipo de datos obsoleto, pero no escribí la aplicación en cuestión.

Philᵀᴹ
fuente

Respuestas:

8

La conversión a nvarchar(max)debería funcionar a menos que esté haciendo algo mal con suCHARINDEX()

Pruebe este fragmento de código, debería generar lo que desea.

-- Create the table
CREATE TABLE [dbo].[PhilsTable](
    [comment] [ntext] NULL,
    [anothercomment] [nvarchar](50) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY];

GO

-- insert very long string
INSERT INTO [dbo].[PhilsTable] (comment, anothercomment) VALUES (N'This is a test UPDATEHERE This is the end of the test' + REPLICATE (CAST(N'x' AS nvarchar(max)), 1000000), 'this goes in here');

-- verify data
SELECT DATALENGTH(comment), *  FROM [dbo].[PhilsTable];

-- perform replace
SELECT CAST(REPLACE(CAST(comment AS NVARCHAR(MAX)),'UPDATEHERE','UPDATEHERE' + anothercomment) AS NTEXT) FROM [dbo].[PhilsTable];

DROP TABLE [dbo].[PhilsTable];

Gracias a Andriy M por ayudar con la REPLICATEdeclaración.

Tom V - prueba topanswers.xyz
fuente
10

Convertir nvarchar(max)y volver a ntexthace que la vida sea más simple desde el punto de vista del código, pero significa convertir y reescribir todo el valor (quizás muy grande), con toda la CPU y la sobrecarga de registro que eso implica.

Una alternativa es usar UPDATETEXT. Esto está en desuso, al igual que ntext, pero puede reducir significativamente la sobrecarga de registro. En el lado negativo, significa usar punteros de texto, y solo funciona en una fila a la vez.

El siguiente código de ejemplo usa un cursor para evitar esa limitación, y lo usa en PATINDEXlugar de, CHARINDEXdado que el primero es una de las pocas funciones que funcionan directamente con ntext:

Data de muestra

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.')
);

Declaración del cursor

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;

Bucle de procesamiento

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;
Paul White 9
fuente