SQL Server - kolom NTEXT dan manipulasi string

11

Saya punya meja dengan NTEXTkolom yang disebut comments. Saya memiliki string kedua, sebut saja anothercomment(a varchar) yang perlu ditempatkan di dalam commentsstring yang diberikan setelah kata UPDATEHERE.

Casting untuk nvarchar(max)memotong commentsstring, jadi saya tidak bisa menggunakan suka CHARINDEX()( Msg 8152, Level 16, State 10, Line 2 String or binary data would be truncated.). Saya telah digunakan datalength()untuk memeriksa bahwa ada beberapa ribu kolom yang> 8000 karakter.

Contoh dari apa yang ingin saya capai (walaupun dengan string lebih lama):

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

komentar lain - . This is inserted.

String yang dihasilkan - This is a test UPDATEHERE. This is inserted. This is the end of the test

Saya menyadari bahwa ini sepele dengan yang normal varchar()/ nvarchar(), tetapi ntextmerupakan mimpi buruk yang lengkap untuk diajak bekerja sama. Saya menyadari bahwa ini adalah tipe data yang sudah usang, tetapi saya tidak menulis aplikasi yang dimaksud.

Philᵀᴹ
sumber

Jawaban:

8

Konversi ke nvarchar(max)harus bekerja kecuali Anda melakukan sesuatu yang salah dengan AndaCHARINDEX()

Coba potongan kode ini, itu akan menampilkan apa yang Anda inginkan.

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

Terima kasih pergi ke Andriy M untuk membantu REPLICATEpernyataan itu.

Tom V - coba topanswers.xyz
sumber
10

Mengubah nvarchar(max)dan kembali ke ntextmemang membuat hidup lebih sederhana dari sudut pandang kode, tetapi itu berarti mengubah dan menulis ulang nilai keseluruhan (mungkin sangat besar), dengan semua CPU dan logging overhead yang menyiratkan.

Alternatifnya adalah menggunakan UPDATETEXT. Ini sudah usang, sama seperti ntext, tetapi dapat mengurangi overhead logging secara signifikan. Pada sisi negatifnya, itu berarti menggunakan petunjuk teks, dan itu hanya beroperasi pada satu baris pada satu waktu.

Kode contoh berikut menggunakan kursor untuk mengatasi keterbatasan itu, dan menggunakan PATINDEXalih-alih CHARINDEXkarena yang pertama adalah salah satu dari beberapa fungsi yang bekerja secara langsung dengan ntext:

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

Deklarasi kursor

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;

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