Comment supprimer tous les caractères non alphabétiques de la chaîne dans SQL Server?

172

Comment pouvez-vous supprimer tous les caractères qui ne sont pas alphabétiques d'une chaîne?

Et les non alphanumériques?

Cela doit-il être une fonction personnalisée ou existe-t-il également des solutions plus généralisables?

Même Mien
la source

Réponses:

362

Essayez cette fonction:

Create Function [dbo].[RemoveNonAlphaCharacters](@Temp VarChar(1000))
Returns VarChar(1000)
AS
Begin

    Declare @KeepValues as varchar(50)
    Set @KeepValues = '%[^a-z]%'
    While PatIndex(@KeepValues, @Temp) > 0
        Set @Temp = Stuff(@Temp, PatIndex(@KeepValues, @Temp), 1, '')

    Return @Temp
End

Appelez ça comme ça:

Select dbo.RemoveNonAlphaCharacters('abc1234def5678ghi90jkl')

Une fois que vous avez compris le code, vous devriez voir qu'il est relativement simple de le modifier pour supprimer également d'autres caractères. Vous pouvez même rendre cela suffisamment dynamique pour passer votre modèle de recherche.

J'espère que ça aide.

George Mastros
la source
9
Ce code supprime les caractères non alpha (donc les nombres sont également supprimés). Si vous voulez laisser les nombres (supprimer les caractères non alphanumériques), alors ... remplacez ^ az par ^ az ^ 0-9 Cette chaîne de recherche apparaît dans le code à deux endroits différents. Assurez-vous de les remplacer tous les deux.
George Mastros
26
D'après le commentaire de Jeff: Je pense que si vous vouliez supprimer toutes les non-lettres et les non-nombres, vous voudriez '^ a-z0-9' (par opposition à '^ az ^ 0-9', ce qui laisserait ^ dans la chaîne) .
Even Mien
1
+1 George. C'est l'un de ces endroits où le code «basé sur les ensembles» et l'utilisation des fonctions scalaires en ligne ont de grandes difficultés à battre ligne par ligne. Bien fait. J'utilise aussi votre fonction "Initial Caps", qui a la même forme de base, depuis quelques années maintenant.
Jeff Moden
6
@Lynchie Change '% [^ az]%' En '% [^ az]%' En gros, il suffit de mettre un espace après le z.
George Mastros
8
Le nom de variable KeepValues ​​est en fait l'opposé de ce qu'il est censé faire. KeepValues ​​répertorie les caractères qui doivent être exclus ..
nee21
167

Version paramétrée de la réponse géniale de G Mastros :

CREATE FUNCTION [dbo].[fn_StripCharacters]
(
    @String NVARCHAR(MAX), 
    @MatchExpression VARCHAR(255)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
    SET @MatchExpression =  '%['+@MatchExpression+']%'

    WHILE PatIndex(@MatchExpression, @String) > 0
        SET @String = Stuff(@String, PatIndex(@MatchExpression, @String), 1, '')

    RETURN @String

END

Alphabétique uniquement:

SELECT dbo.fn_StripCharacters('a1!s2@d3#f4$', '^a-z')

Numérique uniquement:

SELECT dbo.fn_StripCharacters('a1!s2@d3#f4$', '^0-9')

Alphanumérique uniquement:

SELECT dbo.fn_StripCharacters('a1!s2@d3#f4$', '^a-z0-9')

Non alphanumérique:

SELECT dbo.fn_StripCharacters('a1!s2@d3#f4$', 'a-z0-9')
Même Mien
la source
3
Je préfère cette version et j'ai créé mon adaptation de la réponse de G Mastros avant de faire défiler vers le bas pour voter!
Earnshavian
Le modèle regex ne semble pas fonctionner avec tous les espaces. Si je veux supprimer tous les caractères spéciaux, à l'exception des caractères alphanumériques et des espaces, je m'attendrais à utiliser SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^a-z0-9\s')ce qui supprime encore les espaces. J'ai également essayé d'utiliser [[:blank:]]mais cela casse la fonction et rien n'est supprimé de la chaîne. Le plus proche que j'ai obtenu est d'utiliser: SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^a-z0-9 ')(coder en dur un espace dans le modèle regex). Cependant, cela ne supprime pas les sauts de ligne.
Billy McKee
2
@BillyMcKee Ajoutez l'espace au début au lieu de l'ajouter à la fin de l'expression régulière. SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^ a-z0-9')
Mike
8

Croyez-le ou non, dans mon système, cette fonction moche fonctionne mieux que l'élégante G Mastros.

CREATE FUNCTION dbo.RemoveSpecialChar (@s VARCHAR(256)) 
RETURNS VARCHAR(256) 
WITH SCHEMABINDING
    BEGIN
        IF @s IS NULL
            RETURN NULL
        DECLARE @s2 VARCHAR(256) = '',
                @l INT = LEN(@s),
                @p INT = 1

        WHILE @p <= @l
            BEGIN
                DECLARE @c INT
                SET @c = ASCII(SUBSTRING(@s, @p, 1))
                IF @c BETWEEN 48 AND 57
                   OR  @c BETWEEN 65 AND 90
                   OR  @c BETWEEN 97 AND 122
                    SET @s2 = @s2 + CHAR(@c)
                SET @p = @p + 1
            END

        IF LEN(@s2) = 0
            RETURN NULL

        RETURN @s2
J Brune
la source
qu'en est-il des virgules, des points, des espaces, etc.?
sojim
combien diffèrent si vous n'utilisez pas d' ASCIIentier ici et comparez directement la sortie de SUBSTRINGavec certains caractères, par exemple: SET @ch=SUBSTRING(@s, @p, 1)etIF @ch BETWEEN '0' AND '9' OR @ch BETWEEN 'a' AND 'z' OR @ch BETWEEN 'A' AND 'Z' ...
S.Serpooshan
Ajoutez WITH SCHEMABINDING à sa fonction comme votre fonction l'a fait. Vous utilisez VARCHAR, sa fonction utilise NVARCHAR. Si les paramètres que vous passez dans sa fonction sont VARCHAR, vous devez utiliser VARCHAR au lieu de NVARCHAR dans sa fonction, sinon votre système devra convertir les valeurs de chaîne de VARCHAR en NVARCHAR avant de pouvoir exécuter la fonction qui est plus coûteuse. Même avec ces changements, votre fonction pourrait encore être plus rapide, mais ce sont quelques exemples que je peux voir où sa fonction pourrait fonctionner plus lentement pour vous dans votre situation.
EricI
1
Sa fonction utilise également NVARCHAR (MAX) et votre fonction utilise VARCHAR (256). Si 256 est tout ce dont vous avez besoin, changez sa fonction pour utiliser également VARCHAR (256) et sa fonction fonctionnera plus rapidement pour vous.
EricI
5

Je savais que SQL était mauvais pour la manipulation de chaînes, mais je ne pensais pas que ce serait aussi difficile. Voici une fonction simple pour supprimer tous les nombres d'une chaîne. Il y aurait de meilleures façons de le faire, mais c'est un début.

CREATE FUNCTION dbo.AlphaOnly (
    @String varchar(100)
)
RETURNS varchar(100)
AS BEGIN
  RETURN (
    REPLACE(
      REPLACE(
        REPLACE(
          REPLACE(
            REPLACE(
              REPLACE(
                REPLACE(
                  REPLACE(
                    REPLACE(
                      REPLACE(
                        @String,
                      '9', ''),
                    '8', ''),
                  '7', ''),
                '6', ''),
              '5', ''),
            '4', ''),
          '3', ''),
        '2', ''),
      '1', ''),
    '0', '')
  )
END
GO

-- ==================
DECLARE @t TABLE (
    ColID       int,
    ColString   varchar(50)
)

INSERT INTO @t VALUES (1, 'abc1234567890')

SELECT ColID, ColString, dbo.AlphaOnly(ColString)
FROM @t

Production

ColID ColString
----- ------------- ---
    1 abc1234567890 abc

Round 2 - Liste noire basée sur les données

-- ============================================
-- Create a table of blacklist characters
-- ============================================
IF EXISTS (SELECT * FROM sys.tables WHERE [object_id] = OBJECT_ID('dbo.CharacterBlacklist'))
  DROP TABLE dbo.CharacterBlacklist
GO
CREATE TABLE dbo.CharacterBlacklist (
    CharID              int         IDENTITY,
    DisallowedCharacter nchar(1)    NOT NULL
)
GO
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'0')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'1')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'2')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'3')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'4')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'5')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'6')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'7')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'8')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'9')
GO

-- ====================================
IF EXISTS (SELECT * FROM sys.objects WHERE [object_id] = OBJECT_ID('dbo.StripBlacklistCharacters'))
  DROP FUNCTION dbo.StripBlacklistCharacters
GO
CREATE FUNCTION dbo.StripBlacklistCharacters (
    @String nvarchar(100)
)
RETURNS varchar(100)
AS BEGIN
  DECLARE @blacklistCt  int
  DECLARE @ct           int
  DECLARE @c            nchar(1)

  SELECT @blacklistCt = COUNT(*) FROM dbo.CharacterBlacklist

  SET @ct = 0
  WHILE @ct < @blacklistCt BEGIN
    SET @ct = @ct + 1

    SELECT @String = REPLACE(@String, DisallowedCharacter, N'')
    FROM dbo.CharacterBlacklist
    WHERE CharID = @ct
  END

  RETURN (@String)
END
GO

-- ====================================
DECLARE @s  nvarchar(24)
SET @s = N'abc1234def5678ghi90jkl'

SELECT
    @s                  AS OriginalString,
    dbo.StripBlacklistCharacters(@s)   AS ResultString

Production

OriginalString           ResultString
------------------------ ------------
abc1234def5678ghi90jkl   abcdefghijkl

Mon défi aux lecteurs: pouvez-vous rendre cela plus efficace? Qu'en est-il de l'utilisation de la récursivité?

Rob Garrison
la source
vous pourriez probablement écrire un meilleur dbo.StripBlacklistCharacters () sans boucle en utilisant sommarskog.se/arrays-in-sql-2005.html#tblnum table de nombres joints à votre table de liste noire, mais je suis trop paresseux aujourd'hui pour l'essayer moi-même ....
KM.
4

Si vous êtes comme moi et que vous n'avez pas accès à simplement ajouter des fonctions à vos données de production mais que vous souhaitez tout de même effectuer ce type de filtrage, voici une solution purement SQL utilisant une table PIVOT pour rassembler à nouveau les éléments filtrés.

NB J'ai codé en dur le tableau jusqu'à 40 caractères, vous devrez en rajouter si vous avez des chaînes plus longues à filtrer.

SET CONCAT_NULL_YIELDS_NULL OFF;

with 
    ToBeScrubbed
as (
    select 1 as id, '*SOME 222@ !@* #* BOGUS !@*&! DATA' as ColumnToScrub
),

Scrubbed as (
    select 
        P.Number as ValueOrder,
        isnull ( substring ( t.ColumnToScrub , number , 1 ) , '' ) as ScrubbedValue,
        t.id
    from
        ToBeScrubbed t
        left join master..spt_values P
            on P.number between 1 and len(t.ColumnToScrub)
            and type ='P'
    where
        PatIndex('%[^a-z]%', substring(t.ColumnToScrub,P.number,1) ) = 0
)

SELECT
    id, 
    [1]+ [2]+ [3]+ [4]+ [5]+ [6]+ [7]+ [8] +[9] +[10]
    +  [11]+ [12]+ [13]+ [14]+ [15]+ [16]+ [17]+ [18] +[19] +[20]
    +  [21]+ [22]+ [23]+ [24]+ [25]+ [26]+ [27]+ [28] +[29] +[30]
    +  [31]+ [32]+ [33]+ [34]+ [35]+ [36]+ [37]+ [38] +[39] +[40] as ScrubbedData
FROM (
    select 
        *
    from 
        Scrubbed
    ) 
    src
    PIVOT (
        MAX(ScrubbedValue) FOR ValueOrder IN (
        [1], [2], [3], [4], [5], [6], [7], [8], [9], [10],
        [11], [12], [13], [14], [15], [16], [17], [18], [19], [20],
        [21], [22], [23], [24], [25], [26], [27], [28], [29], [30],
        [31], [32], [33], [34], [35], [36], [37], [38], [39], [40]
        )
    ) pvt
Kyle Hale
la source
Cette solution était 2,3 fois plus rapide pour moi que d'utiliser une fonction sur un ensemble de 235K lignes. J'ai également dû faire 2x remplacements et utilisé un total de quatre CTE. A travaillé comme un champion.
JJS
4

Après avoir examiné toutes les solutions données, j'ai pensé qu'il devait y avoir une méthode SQL pure qui ne nécessite pas de fonction ou de requête CTE / XML, et n'implique pas de difficultés à maintenir des instructions REPLACE imbriquées. Voici ma solution:

SELECT 
  x
  ,CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 1, 1) + '%' THEN '' ELSE SUBSTRING(x, 1, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 2, 1) + '%' THEN '' ELSE SUBSTRING(x, 2, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 3, 1) + '%' THEN '' ELSE SUBSTRING(x, 3, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 4, 1) + '%' THEN '' ELSE SUBSTRING(x, 4, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 5, 1) + '%' THEN '' ELSE SUBSTRING(x, 5, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 6, 1) + '%' THEN '' ELSE SUBSTRING(x, 6, 1) END
-- Keep adding rows until you reach the column size 
    AS stripped_column
FROM (SELECT 
        column_to_strip AS x
        ,'ABCDEFGHIJKLMNOPQRSTUVWXYZ' AS a 
      FROM my_table) a

L'avantage de le faire de cette façon est que les caractères valides sont contenus dans la seule chaîne de la sous-requête, ce qui facilite la reconfiguration pour un jeu de caractères différent.

L'inconvénient est que vous devez ajouter une ligne de SQL pour chaque caractère jusqu'à la taille de votre colonne. Pour rendre cette tâche plus facile, je viens d'utiliser le script Powershell ci-dessous, cet exemple si pour un VARCHAR (64):

1..64 | % {
  "    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, {0}, 1) + '%' THEN '' ELSE SUBSTRING(x, {0}, 1) END" -f $_
} | clip.exe
Dave Sexton
la source
3
Gênant dans le cas général, mais facile et utile pour une requête ponctuelle avec une colonne étroite.
Eric J.
3

Voici une autre façon de supprimer les caractères non alphabétiques à l'aide d'un fichier iTVF. Tout d'abord, vous avez besoin d'un séparateur de chaînes basé sur un modèle. En voici un extrait de l' article de Dwain Camp :

-- PatternSplitCM will split a string based on a pattern of the form 
-- supported by LIKE and PATINDEX 
-- 
-- Created by: Chris Morris 12-Oct-2012 
CREATE FUNCTION [dbo].[PatternSplitCM]
(
       @List                VARCHAR(8000) = NULL
       ,@Pattern            VARCHAR(50)
) RETURNS TABLE WITH SCHEMABINDING 
AS 

RETURN
    WITH numbers AS (
        SELECT TOP(ISNULL(DATALENGTH(@List), 0))
            n = ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
        FROM
        (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) d (n),
        (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) e (n),
        (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) f (n),
        (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) g (n)
    )

    SELECT
        ItemNumber = ROW_NUMBER() OVER(ORDER BY MIN(n)),
        Item = SUBSTRING(@List,MIN(n),1+MAX(n)-MIN(n)),
        [Matched]
    FROM (
        SELECT n, y.[Matched], Grouper = n - ROW_NUMBER() OVER(ORDER BY y.[Matched],n)
        FROM numbers
        CROSS APPLY (
            SELECT [Matched] = CASE WHEN SUBSTRING(@List,n,1) LIKE @Pattern THEN 1 ELSE 0 END
        ) y
    ) d
    GROUP BY [Matched], Grouper

Maintenant que vous disposez d'un séparateur basé sur un modèle, vous devez diviser les chaînes qui correspondent au modèle:

[a-z]

puis les concaténer pour obtenir le résultat souhaité:

SELECT *
FROM tbl t
CROSS APPLY(
    SELECT Item + ''
    FROM dbo.PatternSplitCM(t.str, '[a-z]')
    WHERE Matched = 1
    ORDER BY ItemNumber
    FOR XML PATH('')
) x (a)

ÉCHANTILLON

Résultat:

| Id |              str |              a |
|----|------------------|----------------|
|  1 |    testte d'abc |     testtedabc |
|  2 |            anr¤a |           anra |
|  3 |  gs-re-C“te d'ab |     gsreCtedab |
|  4 |         Mfe, DF |          MfeDF |
|  5 |           Rtemd |          Rtemd |
|  6 |          jadji |          jadji |
|  7 |      Cje y ret¢n |       Cjeyretn |
|  8 |        Jklbalu |        Jklbalu |
|  9 |       lene-iokd |       leneiokd |
| 10 |   liode-Pyrnie |    liodePyrnie |
| 11 |         Vs Gta |          VsGta |
| 12 |        Sƒo Paulo |        SoPaulo |
| 13 |  vAstra gAtaland | vAstragAtaland |
| 14 |  ¥uble / Bio-Bio |     ubleBioBio |
| 15 | Upln/ds VAsb-y |    UplndsVAsby |
Félix Pamittan
la source
y a-t-il un avantage à utiliser cela par rapport à d'autres réponses?
S.Serpooshan
2

Cette solution, inspirée de la solution de M. Allen, nécessite une Numberstable d'entiers (que vous devriez avoir sous la main si vous voulez faire des opérations de requêtes sérieuses avec de bonnes performances). Il ne nécessite pas de CTE. Vous pouvez modifier l' NOT IN (...)expression pour exclure des caractères spécifiques ou la remplacer par une expression IN (...)OR LIKEpour ne conserver que certains caractères.

SELECT (
    SELECT  SUBSTRING([YourString], N, 1)
    FROM    dbo.Numbers
    WHERE   N > 0 AND N <= CONVERT(INT, LEN([YourString]))
        AND SUBSTRING([YourString], N, 1) NOT IN ('(',')',',','.')
    FOR XML PATH('')
) AS [YourStringTransformed]
FROM ...
Jay Bienvenu
la source
Solution intéressante à un problème sans rapport.
TaterJuice
2

Voici une solution qui ne nécessite pas de créer une fonction ou de répertorier toutes les instances de caractères à remplacer. Il utilise une instruction WITH récursive en combinaison avec un PATINDEX pour rechercher les caractères indésirables. Il remplacera tous les caractères indésirables dans une colonne - jusqu'à 100 caractères incorrects uniques contenus dans une chaîne donnée. (EG "ABC123DEF234" contiendrait 4 mauvais caractères 1, 2, 3 et 4) La limite de 100 est le nombre maximum de récursions autorisées dans une instruction WITH, mais cela n'impose pas de limite au nombre de lignes à traiter, ce qui n'est limité que par la mémoire disponible.
Si vous ne voulez pas de résultats DISTINCT, vous pouvez supprimer les deux options du code.

-- Create some test data:
SELECT * INTO #testData 
FROM (VALUES ('ABC DEF,K.l(p)'),('123H,J,234'),('ABCD EFG')) as t(TXT)

-- Actual query:
-- Remove non-alpha chars: '%[^A-Z]%'
-- Remove non-alphanumeric chars: '%[^A-Z0-9]%'
DECLARE @BadCharacterPattern VARCHAR(250) = '%[^A-Z]%';

WITH recurMain as (
    SELECT DISTINCT CAST(TXT AS VARCHAR(250)) AS TXT, PATINDEX(@BadCharacterPattern, TXT) AS BadCharIndex
    FROM #testData
    UNION ALL
    SELECT CAST(TXT AS VARCHAR(250)) AS TXT, PATINDEX(@BadCharacterPattern, TXT) AS BadCharIndex
    FROM (
        SELECT 
            CASE WHEN BadCharIndex > 0 
                THEN REPLACE(TXT, SUBSTRING(TXT, BadCharIndex, 1), '')
                ELSE TXT 
            END AS TXT
        FROM recurMain
        WHERE BadCharIndex > 0
    ) badCharFinder
)
SELECT DISTINCT TXT
FROM recurMain
WHERE BadCharIndex = 0;
Emploi Graeme
la source
1

Je mets cela aux deux endroits où PatIndex est appelé.

PatIndex('%[^A-Za-z0-9]%', @Temp)

pour la fonction personnalisée ci-dessus RemoveNonAlphaCharacters et l'a renommée RemoveNonAlphaNumericCharacters

utilisateur381460
la source
1

- Créez d'abord une fonction

CREATE FUNCTION [dbo].[GetNumericonly]
(@strAlphaNumeric VARCHAR(256))
RETURNS VARCHAR(256)
AS
BEGIN
     DECLARE @intAlpha INT
     SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric)
BEGIN
     WHILE @intAlpha > 0
   BEGIN
          SET @strAlphaNumeric = STUFF(@strAlphaNumeric, @intAlpha, 1, '' )
          SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric )
   END
END
RETURN ISNULL(@strAlphaNumeric,0)
END

Appelez maintenant cette fonction comme

select [dbo].[GetNumericonly]('Abhi12shek23jaiswal')

Son résultat comme

1223
Abhishek Jaiswal
la source
1

Du point de vue des performances, j'utiliserais la fonction en ligne:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[udf_RemoveNumericCharsFromString]
(
@List NVARCHAR(4000)
)
RETURNS TABLE 
AS RETURN

    WITH GetNums AS (
       SELECT TOP(ISNULL(DATALENGTH(@List), 0))
        n = ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
        FROM
          (VALUES (0),(0),(0),(0)) d (n),
          (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) e (n),
          (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) f (n),
          (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) g (n)
            )

    SELECT StrOut = ''+
        (SELECT Chr
         FROM GetNums
            CROSS APPLY (SELECT SUBSTRING(@List , n,1)) X(Chr)
         WHERE Chr LIKE '%[^0-9]%' 
         ORDER BY N
         FOR XML PATH (''),TYPE).value('.','NVARCHAR(MAX)')


   /*How to Use
   SELECT StrOut FROM dbo.udf_RemoveNumericCharsFromString ('vv45--9gut')
   Result: vv--gut
   */
Hkravitz
la source
Je sais que ce fil est vieux, mais une fonction de table en ligne est la voie à suivre. Le problème avec votre solution est que, comme vous ne renvoyez que des nombres, ce code:), TYPE) .value ('.', 'NVARCHAR (MAX)') n'est pas nécessaire et ralentira la fonction d'environ 50%
Alan Burstein
1

Voici une autre solution CTE récursive, basée sur la réponse de @Gerhard Weiss ici . Vous devriez pouvoir copier et coller tout le bloc de code dans SSMS et y jouer avec. Les résultats incluent quelques colonnes supplémentaires pour nous aider à comprendre ce qui se passe. Cela m'a pris du temps avant de comprendre tout ce qui se passe avec PATINDEX (RegEx) et le CTE récursif.

DECLARE @DefineBadCharPattern varchar(30)
SET @DefineBadCharPattern = '%[^A-z]%'  --Means anything NOT between A and z characters (according to ascii char value) is "bad"
SET @DefineBadCharPattern = '%[^a-z0-9]%'  --Means anything NOT between a and z characters or numbers 0 through 9 (according to ascii char value) are "bad"
SET @DefineBadCharPattern = '%[^ -~]%'  --Means anything NOT between space and ~ characters (all non-printable characters) is "bad"
--Change @ReplaceBadCharWith to '' to strip "bad" characters from string
--Change to some character if you want to 'see' what's being replaced. NOTE: It must be allowed accoring to @DefineBadCharPattern above
DECLARE @ReplaceBadCharWith varchar(1) = '#'  --Change this to whatever you want to replace non-printable chars with 
IF patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN, @ReplaceBadCharWith) > 0
    BEGIN
        RAISERROR('@ReplaceBadCharWith value (%s) must be a character allowed by PATINDEX pattern of %s',16,1,@ReplaceBadCharWith, @DefineBadCharPattern)
        RETURN
    END
--A table of values to play with:
DECLARE @temp TABLE (OriginalString varchar(100))
INSERT @temp SELECT ' 1hello' + char(13) + char(10) + 'there' + char(30) + char(9) + char(13) + char(10)
INSERT @temp SELECT '2hello' + char(30) + 'there' + char(30)
INSERT @temp SELECT ' 3hello there'
INSERT @temp SELECT ' tab' + char(9) + ' character'
INSERT @temp SELECT 'good bye'

--Let the magic begin:
;WITH recurse AS (
    select
    OriginalString,
    OriginalString as CleanString,
    patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN, OriginalString) as [Position],
    substring(OriginalString,patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN, OriginalString),1) as [InvalidCharacter],
    ascii(substring(OriginalString,patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN, OriginalString),1)) as [ASCIICode]
    from @temp
   UNION ALL
    select
    OriginalString,
    CONVERT(varchar(100),REPLACE(CleanString,InvalidCharacter,@ReplaceBadCharWith)),
    patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString) as [Position],
    substring(CleanString,patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString),1),
    ascii(substring(CleanString,patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString),1))
    from recurse
    where patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString) > 0
)
SELECT * FROM recurse
--optionally comment out this last WHERE clause to see more of what the recursion is doing:
WHERE patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString) = 0
Baodad
la source
0

En utilisant une table de nombres générés par CTE pour examiner chaque caractère, puis FOR XML pour concaténer en une chaîne de valeurs conservées, vous pouvez ...

CREATE FUNCTION [dbo].[PatRemove](
    @pattern varchar(50),
    @expression varchar(8000) 
    )
RETURNS varchar(8000)
AS
BEGIN
    WITH 
        d(d) AS (SELECT d FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) digits(d)),
        nums(n) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM d d1, d d2, d d3, d d4),
        chars(c) AS (SELECT SUBSTRING(@expression, n, 1) FROM nums WHERE n <= LEN(@expression))
    SELECT 
        @expression = (SELECT c AS [text()] FROM chars WHERE c NOT LIKE @pattern FOR XML PATH(''));

    RETURN @expression;
END
Dennis Allen
la source
0
DECLARE @vchVAlue NVARCHAR(255) = 'SWP, Lettering Position 1: 4 Ω, 2: 8 Ω, 3: 16 Ω, 4:  , 5:  , 6:  , Voltage Selector, Solder, 6, Step switch, : w/o fuseholder '


WHILE PATINDEX('%?%' , CAST(@vchVAlue AS VARCHAR(255))) > 0
  BEGIN
    SELECT @vchVAlue = STUFF(@vchVAlue,PATINDEX('%?%' , CAST(@vchVAlue AS VARCHAR(255))),1,' ')
  END 

SELECT @vchVAlue
Mohannad Qattash
la source
0

cette méthode n'a pas fonctionné pour moi car j'essayais de conserver les lettres arabes, j'ai essayé de remplacer l'expression régulière mais cela n'a pas fonctionné non plus. J'ai écrit une autre méthode pour travailler au niveau ASCII car c'était mon seul choix et cela a fonctionné.

 Create function [dbo].[RemoveNonAlphaCharacters] (@s varchar(4000)) returns varchar(4000)
   with schemabinding
begin
   if @s is null
      return null
   declare @s2 varchar(4000)
   set @s2 = ''
   declare @l int
   set @l = len(@s)
   declare @p int
   set @p = 1
   while @p <= @l begin
      declare @c int
      set @c = ascii(substring(@s, @p, 1))
      if @c between 48 and 57 or @c between 65 and 90 or @c between 97 and 122 or @c between 165 and 253 or @c between 32 and 33
         set @s2 = @s2 + char(@c)
      set @p = @p + 1
      end
   if len(@s2) = 0
      return null
   return @s2
   end

ALLER

AGOMAA
la source
-1

Bien que le post soit un peu vieux, je voudrais dire ce qui suit. Le problème que j'ai eu avec la solution ci-dessus est qu'elle ne filtre pas les caractères comme ç, ë, ï, etc. J'ai adapté une fonction comme suit (je n'ai utilisé qu'une chaîne de 80 varchar pour économiser de la mémoire):

create FUNCTION dbo.udf_Cleanchars (@InputString varchar(80)) 
RETURNS varchar(80) 
AS 

BEGIN 
declare @return varchar(80) , @length int , @counter int , @cur_char char(1) 
SET @return = '' 
SET @length = 0 
SET @counter = 1 
SET @length = LEN(@InputString) 
IF @length > 0 
BEGIN WHILE @counter <= @length 

BEGIN SET @cur_char = SUBSTRING(@InputString, @counter, 1) IF ((ascii(@cur_char) in (32,44,46)) or (ascii(@cur_char) between 48 and 57) or (ascii(@cur_char) between 65 and 90) or (ascii(@cur_char) between 97 and 122))
BEGIN SET @return = @return + @cur_char END 
SET @counter = @counter + 1 
END END 

RETURN @return END
Eric
la source
Merci pour cela, Eric. Comme vous le dites, la réponse marquée par le message est très bonne, mais elle ne supprime pas les caractères "numériques" loufoques comme ½.
troy
-3

Je viens de trouver cela intégré à Oracle 10g si c'est ce que vous utilisez. J'ai dû supprimer tous les caractères spéciaux pour comparer un numéro de téléphone.

regexp_replace(c.phone, '[^0-9]', '')
Lu_Bu
la source
5
«SQL Server» fait spécifiquement référence au produit Microsoft.
personne