Couper les espaces (espaces, tabulations, sauts de ligne)


10

Je suis sur SQL Server 2014 et j'ai besoin de nettoyer les espaces blancs du début et de la fin du contenu d'une colonne, où les espaces blancs peuvent être de simples espaces, des tabulations ou des nouvelles lignes (les deux \net \r\n); par exemple

'    this content    '                          should become 'this content'
'  \r\n   \t\t\t this \r\n content \t  \r\n   ' should become 'this \r\n content'

etc.

Je n'ai pu réaliser que le premier cas avec

UPDATE table t SET t.column = LTRIM(RTRIM(t.column))

mais pour les autres cas cela ne fonctionne pas.

Réponses:


8

Pour toute personne utilisant SQL Server 2017 ou plus récent

vous pouvez utiliser la fonction intégrée TRIM . Par exemple:

DECLARE @Test NVARCHAR(4000);
SET @Test = N'  
    ' + NCHAR(0x09) + N'  ' + NCHAR(0x09) + N' this 
 ' + NCHAR(0x09) + NCHAR(0x09) + N'  content' + NCHAR(0x09) + NCHAR(0x09) + N'  
' + NCHAR(0x09) + N' ' + NCHAR(0x09) + NCHAR(0x09) + N'     ';

SELECT N'~'
        + TRIM(NCHAR(0x09) + NCHAR(0x20) + NCHAR(0x0D) + NCHAR(0x0A) FROM @Test)
        + N'~';

Veuillez noter que le comportement par défaut de TRIMest de supprimer uniquement les espaces, afin de supprimer également les tabulations et les sauts de ligne (CR + LF), vous devez spécifier la characters FROMclause.

En outre, j'ai utilisé NCHAR(0x09)pour les caractères de tabulation dans la @Testvariable afin que l'exemple de code puisse être copié-collé et conserver les caractères corrects. Sinon, les tabulations sont converties en espaces lorsque cette page est rendue.

Pour toute personne utilisant SQL Server 2016 ou une version antérieure

Vous pouvez créer une fonction, soit en tant qu'UDF scalaire SQLCLR, soit en tant que TVF en ligne T-SQL (iTVF). Le TVF T-SQL Inline serait le suivant:

CREATE
--ALTER
FUNCTION dbo.TrimChars(@OriginalString NVARCHAR(4000), @CharsToTrim NVARCHAR(50))
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN
WITH cte AS
(
  SELECT PATINDEX(N'%[^' + @CharsToTrim + N']%', @OriginalString) AS [FirstChar],
         PATINDEX(N'%[^' + @CharsToTrim + N']%', REVERSE(@OriginalString)) AS [LastChar],
        LEN(@OriginalString + N'~') - 1 AS [ActualLength]
)
SELECT cte.[ActualLength],
       [FirstChar],
       ((cte.[ActualLength] - [LastChar]) + 1) AS [LastChar],
       SUBSTRING(@OriginalString, [FirstChar],
                 ((cte.[ActualLength] - [LastChar]) - [FirstChar] + 2)) AS [FixedString]
FROM   cte;
GO

Et l'exécuter comme suit:

DECLARE @Test NVARCHAR(4000);
SET @Test = N'  
    ' + NCHAR(0x09) + N'  ' + NCHAR(0x09) + N' this 
 ' + NCHAR(0x09) + NCHAR(0x09) + N'  content' + NCHAR(0x09) + NCHAR(0x09) + N'  
' + NCHAR(0x09) + N' ' + NCHAR(0x09) + NCHAR(0x09) + N'     ';

SELECT N'~' + tc.[FixedString] + N'~' AS [proof]
FROM   dbo.TrimChars(@Test, NCHAR(0x09) + NCHAR(0x20) + NCHAR(0x0D) + NCHAR(0x0A)) tc;

Retour:

proof
----
~this 
              content~

Et vous pouvez l'utiliser dans une UPDATEutilisation CROSS APPLY:

UPDATE tbl
SET    tbl.[Column] = itvf.[FixedString]
FROM   SchemaName.TableName tbl
CROSS APPLY  dbo.TrimChars(tbl.[Column],
                           NCHAR(0x09) + NCHAR(0x20) + NCHAR(0x0D) + NCHAR(0x0A)) itvf

Comme mentionné au début, cela est également très facile via SQLCLR car .NET comprend une Trim()méthode qui effectue exactement l'opération souhaitée . Vous pouvez soit coder le vôtre pour appeler SqlString.Value.Trim(), soit installer la version gratuite de la bibliothèque SQL # (que j'ai créée, mais cette fonction est dans la version gratuite) et utiliser soit String_Trim (qui ne fait que de l'espace blanc) ou String_TrimChars où vous passez les caractères à couper des deux côtés (tout comme l'iTVF illustré ci-dessus).

DECLARE @Test NVARCHAR(4000);
SET @Test = N'  
    ' + NCHAR(0x09) + N'  ' + NCHAR(0x09) + N' this 
 ' + NCHAR(0x09) + NCHAR(0x09) + N'  content' + NCHAR(0x09) + NCHAR(0x09) + N'  
' + NCHAR(0x09) + N' ' + NCHAR(0x09) + NCHAR(0x09) + N'     ';

SELECT N'~' + SQL#.String_Trim(@Test) + N'~' AS [proof];

Et il renvoie exactement la même chaîne que celle montrée ci-dessus dans l'exemple de sortie iTVF. Mais étant un UDF scalaire, vous l'utiliseriez comme suit dans un UPDATE:

UPDATE tbl
SET    tbl.[Column] = SQL#.String_Trim(itvf.[Column])
FROM   SchemaName.TableName tbl

L'une ou l'autre des solutions ci-dessus doit être efficace pour une utilisation sur des millions de lignes. Les TVF en ligne sont optimisables contrairement aux TVF à instructions multiples et aux FDU scalaires T-SQL. De plus, les FDU scalaires SQLCLR peuvent être utilisés dans des plans parallèles, tant qu'ils sont marqués comme IsDeterministic=trueet ne définissent aucun des deux types d'accès aux données Read(la valeur par défaut pour l'accès aux données utilisateur et système est None), et ces deux conditions sont true pour les deux fonctions SQLCLR notées ci-dessus.


4

Vous voudrez peut-être envisager d'utiliser une TVF (fonction de valeur de table) pour supprimer les caractères incriminés du début et de la fin de vos données.

Créez une table pour stocker les données de test:

IF COALESCE(OBJECT_ID('dbo.TrimTest'), 0) <> 0
BEGIN
    DROP TABLE dbo.TrimTest;
END
CREATE TABLE dbo.TrimTest
(
    SampleData VARCHAR(50) NOT NULL
);

INSERT INTO dbo.TrimTest (SampleData)
SELECT CHAR(13) + CHAR(10) + CHAR(9) + 'this is ' + CHAR(13) + CHAR(10) + ' a test' + CHAR(13) + CHAR(10);
GO

Créez le TVF:

IF COALESCE(OBJECT_ID('dbo.StripCrLfTab'), 0) <> 0
BEGIN
    DROP FUNCTION dbo.StripCrLfTab;
END
GO
CREATE FUNCTION dbo.StripCrLfTab
(
    @val NVARCHAR(1000)
)
RETURNS @Results TABLE
(
    TrimmedVal NVARCHAR(1000) NULL
)
AS
BEGIN
    DECLARE @TrimmedVal NVARCHAR(1000);
    SET @TrimmedVal = CASE WHEN RIGHT(@val, 1) = CHAR(13) OR RIGHT(@val, 1) = CHAR(10) OR RIGHT(@val, 1) = CHAR(9)
            THEN LEFT(
                CASE WHEN LEFT(@val, 1) = CHAR(13) OR LEFT(@val, 1) = CHAR(10) OR LEFT(@val, 1) = CHAR(9)
                THEN RIGHT(@val, LEN(@val) - 1)
                ELSE @val
                END
                , LEN(@val) -1 )
            ELSE
                CASE WHEN LEFT(@val, 1) = CHAR(13) OR LEFT(@val, 1) = CHAR(10) OR LEFT(@val, 1) = CHAR(9)
                THEN RIGHT(@val, LEN(@val) - 1)
                ELSE @val
                END
            END;
    IF @TrimmedVal LIKE (CHAR(13) + '%')
        OR @TrimmedVal LIKE (CHAR(10) + '%')
        OR @TrimmedVal LIKE (CHAR(9) + '%')
        OR @TrimmedVal LIKE ('%' + CHAR(13))
        OR @TrimmedVal LIKE ('%' + CHAR(10))
        OR @TrimmedVal LIKE ('%' + CHAR(9))
        SELECT @TrimmedVal = tv.TrimmedVal
        FROM dbo.StripCrLfTab(@TrimmedVal) tv;
    INSERT INTO @Results (TrimmedVal)
    VALUES (@TrimmedVal);
    RETURN;
END;
GO

Exécutez le TVF pour afficher les résultats:

SELECT tt.SampleData
    , stt.TrimmedVal
FROM dbo.TrimTest tt
CROSS APPLY dbo.StripCrLfTab(tt.SampleData) stt;

Résultats:

entrez la description de l'image ici

Le TVF s'appelle récursivement jusqu'à ce qu'il ne reste aucun caractère incriminé au début et à la fin de la chaîne passée dans la fonction. Il est peu probable que cela fonctionne bien sur un grand nombre de lignes, mais cela fonctionnerait probablement bien si vous utilisez cela pour corriger les données lors de leur insertion dans la base de données.

Vous pouvez l'utiliser dans une déclaration de mise à jour:

UPDATE dbo.TrimTest
SET TrimTest.SampleData = stt.TrimmedVal
FROM dbo.TrimTest tt
CROSS APPLY dbo.StripCrLfTab(tt.SampleData) stt;


SELECT *
FROM dbo.TrimTest;

Résultats (sous forme de texte):

entrez la description de l'image ici


Merci Max, malheureusement je dois nettoyer une grande quantité de lignes (millions) dans plusieurs tables, j'espérais que certaines fonctions seraient utilisées dans une UPDATErequête comme LTRIM/ RTRIM, quelque chose dans les lignes de UPDATE table t SET t.column = TRIM(t.column, CONCAT(CHAR(9), CHAR(10), CHAR(13)))avec une TRIM( expression, charlist )fonction acceptant une liste de caractères à rogner comme l'ont fait de nombreux langages de script.
Giovanni Lovato

L'avertissement que j'ai donné à ce sujet "probablement" ne fonctionne pas bien sur de nombreuses lignes peut ou peut ne pas être un problème. Si vous ne faites cela qu'une seule fois, ce n'est peut-être pas un problème. Vous voudrez peut-être le tester dans un environnement hors production afin de voir combien de temps cela prend.
Max Vernon

Je mettrai à jour ma réponse pour montrer comment vous utiliseriez cela dans une updatedéclaration.
Max Vernon

1

J'ai juste eu un problème avec cette situation particulière, j'avais besoin de trouver et de nettoyer chaque champ avec des espaces blancs, mais j'ai trouvé 4 types d'espaces blancs possibles dans mes champs de base de données (référence à la table de code ASCII):

  • Onglet horizontal (caractère (9))
  • Nouvelle ligne (char (10))
  • Onglet vertical (char (9))
  • Espace (char (32))

Peut-être que cette requête peut vous aider.

UPDATE @TABLE SET @COLUMN = replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')

Cela nettoie également les espaces blancs au milieu des champs, pas seulement le début et la fin comme demandé dans la question.
Colin 't Hart

Oui, vous avez raison, je vais éditer
sami.almasagedi

-1

Vous devez analyser le deuxième exemple car LTRIM / RTRIM ne coupe que les espaces. Vous voulez réellement couper ce que SQL considère comme des données (/ r, / t, etc.). Si vous connaissez les valeurs que vous recherchez, utilisez simplement REMPLACER pour les remplacer. Mieux encore, écrivez une fonction et appelez-la.


-1

Si vous le souhaitez, utilisez ma fonction élégante:

CREATE FUNCTION s_Trim
(
    @s nvarchar(max)
)
RETURNS nvarchar(max)
AS
BEGIN
    -- Create comparators for LIKE operator
    DECLARE @whitespaces nvarchar(50) = CONCAT('[ ', CHAR(9), CHAR(10), CHAR(13), ']'); -- Concat chars that you consider as whitespaces
    DECLARE @leftComparator nvarchar(50) = @whitespaces + '%',
            @rightComparator nvarchar(50) = '%' + @whitespaces;
    -- LTRIM
    WHILE @s LIKE @leftComparator AND LEN(@s + 'x') > 1 SET @s = RIGHT(@s, LEN(@s + 'x') - 2)
    -- RTRIM
    WHILE @s LIKE @rightComparator AND LEN(@s + 'x') > 1 SET @s = LEFT(@s, LEN(@s + 'x') - 2)

    RETURN @s;
END
GO

1
Les fonctions à valeur scalaire ne sont guère élégantes. Ils forcent les requêtes à s'exécuter en série et à s'exécuter une fois par ligne (et non une fois par requête). Vous devriez plutôt regarder les fonctions de valeur de table en ligne.
Erik Darling

-2

L'utilisation de la fonction sur des données volumineuses peut prendre un certain temps d'exécution. J'ai un ensemble de données de 8 millions de lignes, l'utilisation de la fonction a pris plus de 30 minutes à exécuter. replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')n'a pris que 5 sec. Merci a tous. Je te vois @ sami.almasagedi et @Colin 't Hart


Comme dans la réponse que vous répétez, cela ne résout pas le problème si les espaces entre les premier et dernier caractères non blancs doivent être conservés. La vitesse n'est utile que lorsqu'elle donne la réponse souhaitée. Voir aussi les notes dans la réponse acceptée sur la façon de s'assurer que les fonctions ne ralentissent pas une requête comme celle-ci.
RDFozz
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.