Substituir caracteres especiais em uma coluna por espaço

10

Estou tentando escrever uma consulta que substitui os caracteres especiais por espaço. O código abaixo ajuda a identificar as linhas. (caracteres alfanuméricos, vírgula e espaço são válidos):

SELECT columnA
FROM tableA
WHERE columnA like '%[^a-Z0-9, ]%'

Como posso integrar a função de substituição na instrução select para que todos os caracteres, exceto alfanuméricos, vírgulas e espaço no conjunto de resultados, sejam substituídos por '' (espaço). Este não vai funcionar:

SELECT replace(columnA,'%[^a-Z0-9, ]%',' ')
FROM tableA
WHERE columnA like '%[^a-Z0-9, ]%'
Stackoverflowuser
fonte

Respostas:

11

Se você tiver a garantia de usar apenas as 26 letras do alfabeto em inglês dos EUA (versões em maiúsculas e minúsculas), com certeza, poderá usar LIKEe / ou PATINDEXa notação simples de faixa [a-z](você não usaria precisa usar um "Z" maiúsculo ao usar um agrupamento que não diferencia maiúsculas de minúsculas).

Mas, se você pode obter caracteres não encontrados na casa de US alfabeto ainda disponíveis em várias páginas de código / agrupamentos de VARCHARdados (por exemplo Þ= capitais Latina "Thorn" = SELECT CHAR(0xDE)), então você pode precisar incluir aquelas na classe de caracteres: [a-z0-9, Þ]. Obviamente, o que esses caracteres extras seriam é por página de código.

Além disso, esteja ciente de que tanto o tipo de agrupamento (SQL Server x Windows) quanto as configurações de sensibilidade (maiúsculas e minúsculas, sotaque etc. sensíveis versus não sensíveis) afetarão quais caracteres estão incluídos em um determinado intervalo. Por exemplo, os agrupamentos do SQL Server classificam letras maiúsculas e minúsculas na ordem oposta aos agrupamentos do Windows. Ou seja, assumindo um agrupamento que diferencia maiúsculas de minúsculas para ambos os tipos de agrupamentos, um fará AaBb...e o outro fará aAbB.... O efeito será que aestará dentro do intervalo de A-Zum deles, mas não do outro. E o intervalo de a-Znão corresponderá a nenhum caractere em um agrupamento binário (um que termina em um _BINou _BIN2, mas não usa _BIN), considerando que o valor de A65 eaé 97, portanto, é um intervalo inválido de 97 a 65 ;-). Existem muitas variações para dar exemplos aqui, então tentarei postar uma explicação detalhada em meu blog em breve (e atualizarei isso com o link para ele). No entanto, se você for rigoroso em aceitar apenas caracteres em inglês dos EUA (mesmo que receba cartas válidas de outros idiomas), sua melhor opção provavelmente será usar o seguinte padrão e agrupamento:

LIKE '%[^A-Za-z0-9, ]%' COLLATE Latin1_General_100_BIN2

Agora, se você NVARCHARoferece suporte a dados e pode obter caracteres "word" de vários idiomas, o T-SQL não será de grande ajuda, pois não há uma maneira real de diferenciar essas coisas. Nesse caso, você deve usar uma Expressão Regular (RegEx) - especificamente o Replacemétodo / função - e essas estão disponíveis apenas através do SQLCLR. A seguir, é mostrado um exemplo de substituição de vários caracteres "especiais", deixando todas as letras válidas em pelo menos um idioma:

DECLARE @Test NVARCHAR(500);
SET @Test = N'this$is%a<>TEST,;to}⌡↕strip╞╟╚══¶out_ç_ƒ▀ special-ij-೫-chars-舛-დ-א-B';
SELECT SQL#.RegEx_Replace4k(@Test, N'[\W\p{Pc}-[,]]', N' ', -1, 1, NULL); 

Devoluções:

this is a  TEST, to   strip      out ç ƒ  special ij ೫ chars 舛 დ א B

A expressão RegEx significa:

  • \W= um "escape" RegEx que significa "qualquer caractere não- palavra"
  • \p{Pc}= uma "categoria" Unicode de "Pontuação, conector" (isso é necessário para a correspondência apenas porque essa "categoria" é especificamente excluída pela \Wfuga)
  • -[,]= subtração de classe (isso é necessário para excluir vírgulas da correspondência como "especial", pois elas estão incluídas na \Wfuga)

Você pode fazer uma atualização de uma tabela simplesmente emitindo:

UPDATE tbl
SET    tbl.field = SQL#.RegEx_Replace4k(tbl.field, N'[\W\p{Pc}-[,]]', N' ', -1, 1, NULL)
FROM   tbl
WHERE  SQL#.RegEx_IsMatch4k(tbl.field, N'[\W\p{Pc}-[,]]', 1, NULL) = 1;

Observe que, para esses exemplos, usei duas funções disponíveis na biblioteca SQL # versão gratuita das funções SQLCLR, que eu criei (mas, novamente, elas são gratuitas). Observe também que usei as versões "4k", que são mais rápidas devido ao uso em NVARCHAR(4000)vez dos NVARCHAR(MAX)tipos de parâmetros. Se seus dados estiverem sendo usados NVARCHAR(MAX), remova o "4k" dos nomes das funções.

Veja também:

Solomon Rutzky
fonte
5

Eu tenho um post aqui que faz algo semelhante .

Basicamente, estou usando um CTE recursivo para repetir repetidamente um caractere "ruim" de cada vez. Estou usando STUFF para remover um caractere (embora você possa substituí-lo por um espaço) e PATINDEX para encontrar o local do caractere que quero remover. Você pode modificá-lo um pouco para fazer o que está procurando. No entanto, ele cria uma lista "boa", na verdade não atualiza a lista existente.

DECLARE @Pattern varchar(50) = '%[^A-Za-z0-9, ]%';

WITH FixBadChars AS (SELECT StringToFix, StringToFix AS FixedString, 1 AS MyCounter, Id
                FROM BadStringList
                UNION ALL
                SELECT StringToFix, Stuff(FixedString, PatIndex(@Pattern, 
                    FixedString COLLATE Latin1_General_BIN2), 1, ' ') AS FixedString, 
                    MyCounter + 1, Id
                FROM FixBadChars
                WHERE FixedString COLLATE Latin1_General_BIN2 LIKE @Pattern)
SELECT StringToFix, FixedString, MyCounter, Id
FROM FixBadChars
WHERE MyCounter = 
        (SELECT MAX(MyCounter) 
        FROM FixBadChars Fixed
        WHERE Fixed.Id = FixBadChars.Id)
OPTION (MAXRECURSION 1000);

Você deve poder modificar a parte inferior para fazer uma atualização, em vez de apenas uma consulta, mas na verdade não tentei. Tenho certeza de que seria algo parecido com isto:

UPDATE FixBadChars
SET StringToFix = FixedString
WHERE MyCounter = 
        (SELECT MAX(MyCounter) 
        FROM FixBadChars Fixed
        WHERE Fixed.Id = FixBadChars.Id)
OPTION (MAXRECURSION 1000);

Quanto à escalabilidade, retornei ~ 170k linhas limpas em menos de 30 segundos. Mais uma vez não tenho certeza sobre como fazer uma atualização, mas isso foi no meu laptop, que é bastante lento, com apenas 6GB de RAM.

Kenneth Fisher
fonte
0
Declare @String nchar(2000)='hg$%^AB,.:23ab-=+'

Declare @NewString VARCHAR(2000)=''
Declare @Lenght int=LEN(@String)
Declare @Index int=1

WHILE (@Index <= @Lenght)
BEGIN
    Declare @Letter nchar(1)=Substring(@String,@Index,1);
    Declare @ASCII int=ASCII(@Letter);
    If((@ASCII >= 48 and @ASCII <= 57) or (@ASCII >= 97 and @ASCII <= 122) or (@ASCII >= 65 and @ASCII <= 90))
    BEGIN
        SET @NewString += @Letter
    END
    ELSE
    BEGIN
        SET @NewString += ' '
    END
    SET @Index+=1

END
Select @NewString
William Mendoza
fonte