web-dev-qa-db-ja.com

SQL Server-NTEXT列と文字列操作

NTEXTという名前のcomments列を持つテーブルがあります。 2番目の文字列があります。それをanothercomment(a varchar)と呼びましょう。これは、単語commentsの後に、指定されたUPDATEHERE文字列内に配置する必要があります。

nvarchar(max)にキャストするとcomments文字列が切り捨てられるため、CHARINDEX()(_Msg 8152, Level 16, State 10, Line 2 String or binary data would be truncated.)_などを使用できません。datalength()を使用しました> 8000文字の数千の列があることを確認します。

(はるかに長い文字列ではありますが)私が達成したいことの例:

コメント-_This is a test UPDATEHERE This is the end of the test_

別のコメント-_. This is inserted._

結果の文字列-_This is a test UPDATEHERE. This is inserted. This is the end of the test_

これは通常のvarchar()/nvarchar()の場合は取るに足らないことだと思いますが、ntextは完全に動作する完全な悪夢です。私はそれが非推奨のデータ型であることを理解していますが、問題のアプリケーションを記述していません。

11
Philᵀᴹ

nvarchar(max)で問題が発生しない限り、CHARINDEX()への変換は機能するはずです。

このコードスニペットを試してください。必要なものが出力されるはずです。

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

REPLICATEステートメントを手伝ってくれて Andriy M に感謝します。

nvarchar(max)に変換し、ntextに戻すと、コードの観点からは簡単になりますが、すべての(おそらく非常に大きい)値を変換および書き換えることを意味し、すべてのCPUとロギングオーバーヘッドが意味します。

UPDATETEXT を使用することもできます。これは、ntextと同様に非推奨ですが、ログのオーバーヘッドを大幅に削減できます。欠点は、テキストポインターを使用することを意味し、一度に1行のみを操作します。

次のコード例では、カーソルを使用してその制限を回避し、PATINDEXの代わりにCHARINDEXを使用しています。前者は いくつかの関数 の1つであるため、 ntext

サンプルデータ

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

カーソル宣言

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;

処理ループ

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