SQL Server: colonne NTEXT e manipolazione di stringhe


11

Ho una tabella con una NTEXTcolonna chiamata comments. Ho una seconda stringa, chiamiamola anothercomment(a varchar) che deve essere inserita in una determinata commentsstringa dopo la parola UPDATEHERE.

Casting per nvarchar(max)troncare la commentsstringa, quindi non posso usare Mi piace di CHARINDEX()( Msg 8152, Level 16, State 10, Line 2 String or binary data would be truncated.). Ho usato datalength()per verificare che ci siano alcune migliaia di colonne che sono> 8000 caratteri.

Un esempio di ciò che voglio ottenere (anche se con stringhe molto più lunghe):

Commenti - This is a test UPDATEHERE This is the end of the test

altro commento - . This is inserted.

Stringa risultante - This is a test UPDATEHERE. This is inserted. This is the end of the test

Mi rendo conto che questo è banale con un normale varchar()/ nvarchar(), ma ntextè un incubo completo e totale con cui lavorare. Mi rendo conto che è un tipo di dati obsoleto, ma non ho scritto l'applicazione in questione.

Risposte:


8

La conversione in nvarchar(max)dovrebbe funzionare a meno che tu non stia facendo qualcosa di sbagliato nel tuoCHARINDEX()

Prova questo snippet di codice, dovrebbe generare ciò che desideri.

-- 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];

Grazie a Andriy M per l'aiuto con la REPLICATEdichiarazione.


10

La conversione in nvarchar(max)e ntextviceversa semplifica la vita da un punto di vista del codice, ma significa convertire e riscrivere l'intero valore (forse molto grande), con tutta la CPU e il sovraccarico di registrazione che implica.

Un'alternativa è usare UPDATETEXT. Questo è deprecato, proprio come ntext, ma può ridurre significativamente l'overhead di registrazione. Sul lato negativo, significa utilizzare i puntatori di testo e funziona solo su una riga alla volta.

Il codice di esempio seguente utilizza un cursore per aggirare quella limitazione e utilizza PATINDEXinvece che CHARINDEXpoiché la prima è una delle poche funzioni che funzionano direttamente con ntext:

Dati di esempio

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

Dichiarazione del cursore

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;

Ciclo di elaborazione

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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.