SQL Server - NTEXT-Spalten und Zeichenfolgenmanipulation

11

Ich habe eine Tabelle mit einer NTEXTSpalte namens comments. Ich habe eine zweite Zeichenfolge, nennen wir sie anothercomment(a varchar), die commentsnach dem Wort in eine bestimmte Zeichenfolge eingefügt werden muss UPDATEHERE.

Beim Casting wird nvarchar(max)die commentsZeichenfolge abgeschnitten , sodass ich keine ähnlichen Zeichen verwenden kann CHARINDEX()( Msg 8152, Level 16, State 10, Line 2 String or binary data would be truncated.). Ich habe verwendet datalength(), um zu überprüfen, ob einige tausend Spalten mit mehr als 8000 Zeichen vorhanden sind.

Ein Beispiel für das, was ich erreichen möchte (wenn auch mit viel längeren Saiten):

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

ein weiterer Kommentar - . This is inserted.

Resultierende Zeichenfolge - This is a test UPDATEHERE. This is inserted. This is the end of the test

Mir ist klar, dass dies bei einem normalen varchar()/ trivial ist nvarchar(), aber ntextein kompletter und völliger Albtraum ist, mit dem man arbeiten muss. Mir ist klar, dass es sich um einen veralteten Datentyp handelt, aber ich habe die betreffende Anwendung nicht geschrieben.

Philᵀᴹ
quelle

Antworten:

8

Das Konvertieren in nvarchar(max)sollte funktionieren, es sei denn, Sie machen etwas falsch mit IhremCHARINDEX()

Versuchen Sie dieses Code-Snippet, es sollte ausgeben, was Sie wollen.

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

Vielen Dank an Andriy M für die Hilfe bei der REPLICATEAussage.

Tom V - versuchen Sie topanswers.xyz
quelle
10

Das Konvertieren in nvarchar(max)und zurück in ntextvereinfacht das Leben aus Code-Sicht, bedeutet jedoch, dass der gesamte (möglicherweise sehr große) Wert mit dem gesamten damit verbundenen CPU- und Protokollierungsaufwand konvertiert und neu geschrieben wird.

Eine Alternative ist zu verwenden UPDATETEXT. Dies ist zwar veraltet, ntextkann jedoch den Protokollierungsaufwand erheblich reduzieren. Auf der anderen Seite bedeutet dies, dass Textzeiger verwendet werden und jeweils nur eine Zeile bearbeitet wird.

Der folgende Beispielcode verwendet einen Cursor, um diese Einschränkung zu umgehen, und verwendet PATINDEXstattdessen, CHARINDEXda erstere eine der wenigen Funktionen ist , mit denen direkt gearbeitet wird ntext:

Beispieldaten

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-Deklaration

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;

Verarbeitungsschleife

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
quelle