SQL Server - Colonnes NTEXT et manipulation de chaînes


11

J'ai une table avec une NTEXTcolonne appelée comments. J'ai une deuxième chaîne, appelons-la anothercomment(a varchar) qui doit être placée à l'intérieur d'une commentschaîne donnée après le mot UPDATEHERE.

Casting pour nvarchar(max)tronquer la commentschaîne, donc je ne peux pas utiliser les goûts de CHARINDEX()( Msg 8152, Level 16, State 10, Line 2 String or binary data would be truncated.). J'ai utilisé datalength()pour vérifier qu'il y a quelques milliers de colonnes qui sont> 8000 caractères.

Un exemple de ce que je veux réaliser (bien qu'avec des cordes beaucoup plus longues):

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

autre commentaire - . This is inserted.

Chaîne résultante - This is a test UPDATEHERE. This is inserted. This is the end of the test

Je me rends compte que c'est trivial avec un normal varchar()/ nvarchar(), mais ntextc'est un cauchemar complet et absolu avec lequel travailler. Je me rends compte que c'est un type de données obsolète, mais je n'ai pas écrit l'application en question.

Réponses:


8

La conversion en nvarchar(max)devrait fonctionner, sauf si vous faites quelque chose de mal avec votreCHARINDEX()

Essayez cet extrait de code, il devrait produire ce que vous voulez.

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

Merci à Andriy M pour sa contribution à la REPLICATEdéclaration.


10

La conversion en nvarchar(max)et en retour ntextsimplifie la vie du point de vue du code, mais cela signifie la conversion et la réécriture de la valeur entière (peut-être très grande), avec tout le CPU et la surcharge de journalisation que cela implique.

Une alternative est d'utiliser UPDATETEXT. C'est obsolète, tout comme ntext, mais cela peut réduire considérablement les frais de journalisation. À la baisse, cela signifie utiliser des pointeurs de texte, et cela ne fonctionne que sur une ligne à la fois.

L'exemple de code suivant utilise un curseur pour contourner cette limitation, et utilise PATINDEXau lieu de CHARINDEXpuisque la première est l'une des rares fonctions qui fonctionnent directement avec ntext:

Exemples de données

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

Déclaration du curseur

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;

Boucle de traitement

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;
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.