Como remover todos os caracteres não alfabéticos da string no SQL Server?

172

Como você pode remover todos os caracteres que não são alfabéticos de uma string?

E quanto a não alfanuméricos?

Isso precisa ser uma função personalizada ou também existem soluções mais generalizáveis?

Even Mien
fonte

Respostas:

362

Tente esta função:

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

Chame assim:

Select dbo.RemoveNonAlphaCharacters('abc1234def5678ghi90jkl')

Depois de entender o código, você deve ver que é relativamente simples alterá-lo para remover outros caracteres também. Você pode até tornar isso dinâmico o suficiente para transmitir seu padrão de pesquisa.

Espero que ajude.

George Mastros
fonte
9
Esse código remove caracteres não alfa (portanto, os números também são removidos). Se você quiser deixar os números (remover caracteres numéricos não alfa), substitua ^ az por ^ az ^ 0-9 Essa cadeia de pesquisa aparece no código em dois lugares diferentes. Certifique-se de substituir os dois.
George Mastros 17/06/09
26
Do comentário de Jeff: Eu acho que se você quisesse remover todas as letras e números, você iria querer '^ a-z0-9' (versus '^ az ^ 0-9', o que deixaria ^ na string) .
Mesmo Mien
1
+1 George. Esse é um daqueles lugares em que o código "baseado em conjunto" e o uso de funções escalares em linha têm grande dificuldade em superar a linha por linha. Bem feito. Eu também uso a função "Initial Caps", que tem a mesma forma básica, há alguns anos.
9118 Jeff Moden
6
@Lynchie Change '% [^ az]%' Para '% [^ az]%' Basicamente, basta colocar um caractere de espaço após z.
George Mastros
8
O nome da variável KeepValues ​​é realmente o oposto do que ele deve fazer. KeepValues lista os caracteres que precisam ser excluídos ..
nee21
167

Versão parametrizada do G Mastros ' resposta incrível :

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

Apenas alfabético:

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

Apenas numérico:

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

Apenas alfanumérico:

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

Não alfanumérico:

SELECT dbo.fn_StripCharacters('a1!s2@d3#f4$', 'a-z0-9')
Even Mien
fonte
3
Prefiro esta versão e criei minha adaptação da resposta de G Mastros antes de rolar para baixo para votar!
earnshavian
O padrão regex parece não funcionar com todos os espaços em branco. Se eu quiser retirar todos os caracteres especiais, exceto caracteres alfanuméricos e espaços em branco, esperaria usar o SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^a-z0-9\s')que ainda retira os espaços em branco. Eu também tentei usar, [[:blank:]]mas isso interrompe a função e nada é removido da string. O mais próximo que eu cheguei é usando: SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^a-z0-9 ')(codificação embutida de um espaço no padrão regex). No entanto, isso não remove as quebras de linha.
Billy McKee
2
@BillyMcKee Adicione o espaço no início, em vez de adicioná-lo ao final da expressão regular. SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^ a-z0-9')
1955 Mike
8

Acredite ou não, no meu sistema essa função feia tem melhor desempenho do que a elegante 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
fonte
e vírgulas, pontos, espaço etc.?
sojim
quanto difere se você não usar ASCIIinteiro aqui e comparar diretamente a saída de SUBSTRINGcom alguns caracteres, por exemplo: SET @ch=SUBSTRING(@s, @p, 1)eIF @ch BETWEEN '0' AND '9' OR @ch BETWEEN 'a' AND 'z' OR @ch BETWEEN 'A' AND 'Z' ...
S.Serpooshan
Adicione WITH SCHEMABINDING à função dele como a sua função. Você está usando VARCHAR, a função dele está usando NVARCHAR. Se os parâmetros que você está passando para a função dele são VARCHAR, você deve usar VARCHAR em vez de NVARCHAR dentro da função, caso contrário, seu sistema precisará converter os valores de string de VARCHAR para NVARCHAR antes de poder executar a função que é mais cara. Mesmo com essas mudanças, sua função ainda pode ser mais rápida, mas esses são alguns exemplos que posso ver onde a função dele pode ser mais lenta na sua situação.
EricI
1
Sua função também está usando NVARCHAR (MAX) e sua função está usando VARCHAR (256). Se 256 for tudo o que você precisa, altere a função dele para usar também o VARCHAR (256) e a função dele funcionará mais rápido para você.
EricI
5

Eu sabia que o SQL era ruim na manipulação de strings, mas não achei que seria tão difícil. Aqui está uma função simples para remover todos os números de uma string. Haveria maneiras melhores de fazer isso, mas isso é um começo.

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

Resultado

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

Rodada 2 - Lista negra orientada por dados

-- ============================================
-- 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

Resultado

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

Meu desafio para os leitores: você pode tornar isso mais eficiente? Que tal usar recursão?

Rob Garrison
fonte
você provavelmente poderia escrever um dbo.StripBlacklistCharacters () melhor sem um loop usando a tabela de números sommarskog.se/arrays-in-sql-2005.html#tblnum que se juntou à sua lista negra, mas hoje estou com preguiça de experimentá-lo eu ....
KM.
4

Se você é como eu e não tem acesso apenas para adicionar funções aos dados de produção, mas ainda deseja executar esse tipo de filtragem, aqui está uma solução SQL pura usando uma tabela PIVOT para reunir novamente as peças filtradas.

NB : codifiquei a tabela com até 40 caracteres, você precisará adicionar mais se tiver mais seqüências para filtrar.

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
fonte
Essa solução foi 2,3x mais rápida para mim do que usar uma função em um conjunto de 235K linhas. Eu também tive que fazer substituições 2x e usei um total de quatro CTEs. Trabalhou como um campeão.
JJS 28/04
4

Tendo analisado todas as soluções fornecidas, pensei que deveria haver um método SQL puro que não requer uma função ou uma consulta CTE / XML e não envolve dificuldade em manter instruções REPLACE aninhadas. Aqui está a minha solução:

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

A vantagem de fazer isso dessa maneira é que os caracteres válidos estão contidos em uma sequência na subconsulta, facilitando a reconfiguração para um conjunto diferente de caracteres.

A desvantagem é que você precisa adicionar uma linha de SQL para cada caractere até o tamanho da sua coluna. Para facilitar essa tarefa, usei o script Powershell abaixo, neste exemplo, para um 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
fonte
3
Estranho no caso geral, mas fácil e útil para uma consulta única com uma coluna estreita.
Eric J.
3

Aqui está outra maneira de remover caracteres não alfabéticos usando um iTVF. Primeiro, você precisa de um divisor de string baseado em padrão. Aqui está um artigo do artigo 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

Agora que você possui um divisor baseado em padrão, é necessário dividir as strings que correspondem ao padrão:

[a-z]

e concatená-los de volta para obter o resultado desejado:

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)

AMOSTRA

Resultado:

| 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 |
Felix Pamittan
fonte
existe alguma vantagem em usar isso em relação a outras respostas?
S.Serpooshan
2

Essa solução, inspirada na solução do Sr. Allen, requer uma Numberstabela de números inteiros (que você deve ter em mãos se quiser executar operações de consulta sérias com bom desempenho). Não requer um CTE. Você pode alterar a NOT IN (...)expressão para excluir caracteres específicos ou alterá-la para uma expressão IN (...)OR LIKEpara reter apenas determinados caracteres.

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
fonte
Solução interessante para um problema não relacionado.
TaterJuice 9/04
2

Aqui está uma solução que não requer a criação de uma função ou a listagem de todas as instâncias de caracteres a serem substituídas. Ele usa uma instrução WITH recursiva em combinação com um PATINDEX para encontrar caracteres indesejados. Ele substituirá todos os caracteres indesejados em uma coluna - até 100 caracteres inválidos únicos contidos em qualquer sequência. (EG "ABC123DEF234" conteria 4 caracteres inválidos 1, 2, 3 e 4) O limite de 100 é o número máximo de recursões permitidas em uma instrução WITH, mas isso não impõe um limite no número de linhas a serem processadas, o que é limitado apenas pela memória disponível.
Se você não deseja resultados DISTINCT, pode remover as duas opções do código.

-- 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;
Graeme Job
fonte
1

Coloquei isso nos dois lugares em que PatIndex é chamado.

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

para a função personalizada acima RemoveNonAlphaCharacters e renomeou-a RemoveNonAlphaNumericCharacters

user381460
fonte
1

--Primeiro crie uma função

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

Agora chame essa função como

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

Seu resultado como

1223
Abhishek Jaiswal
fonte
1

Do ponto de vista de desempenho, eu usaria a função embutida:

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
fonte
Eu sei que esse segmento é antigo, mas uma função com valor de tabela embutida é o caminho a percorrer. O problema da sua solução é que, como você está retornando apenas números, esse código:), TYPE) .value ('.', 'NVARCHAR (MAX)') não é necessário e diminui a velocidade da função em ~ 50%
Alan Burstein
1

Aqui está outra solução CTE recursiva, baseada na resposta de @Gerhard Weiss aqui . Você deve copiar e colar todo o bloco de código no SSMS e brincar com ele lá. Os resultados incluem algumas colunas extras para nos ajudar a entender o que está acontecendo. Demorei um pouco até entender tudo o que estava acontecendo com o PATINDEX (RegEx) e o CTE recursivo.

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
fonte
0

Usando uma tabela de números gerados em CTE para examinar cada caractere, FOR XML para concat com uma cadeia de valores mantidos, você pode ...

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
fonte
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
fonte
0

Dessa forma, não funcionou para mim, pois estava tentando manter as letras árabes. Tentei substituir a expressão regular, mas também não funcionou. Eu escrevi outro método para trabalhar no nível ASCII, pois era minha única opção e funcionava.

 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

IR

AGOMAA
fonte
-1

Embora o post seja um pouco antigo, gostaria de dizer o seguinte. O problema que tive com a solução acima é que ele não filtra caracteres como ç, ë, ï etc. Adaptei uma função da seguinte maneira (usei apenas uma string varchar 80 para economizar memória):

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
fonte
Obrigado por isso, Eric. Como você diz, a resposta marcada na postagem é muito boa, mas não retira caracteres "numéricos" engraçados como ½.
troy
-3

Acabei de encontrar isso embutido no Oracle 10g, se é isso que você está usando. Eu tive que retirar todos os caracteres especiais para comparar o número de telefone.

regexp_replace(c.phone, '[^0-9]', '')
Lu Bu
fonte
5
"SQL Server" refere-se especificamente ao produto da Microsoft.
ninguém