Existe um equivalente T-SQL para pontuação, pois [0-9] é para números e [az] é para letras?

8

Existe um equivalente T-SQL dos padrões [0-9]e [a-z]que me permitirá extrair valores de uma coluna que contém pontuação?

Por exemplo:

Create Table #Test
(
Value   VarChar(10)
) 
Insert Into #Test
Values ('123a'), ('456b'), ('12ABC'),('AB!23'),('C?D789')

Select      *
From        #Test
Where       Value like '[0-9][0-9][0-9][a-z]'

Isso retornaria valores em que os três primeiros caracteres são números entre 0 e 9 e o último caractere será uma letra entre a e z; portanto, retornaria coisas como 123ae 456bmas não retornaria um valor de 12ABC.

Quero saber se existe um equivalente para pontuação, assim como [0-9]para números e [a-z]letras, para que retorne AB!23e C?D789?

Se eu pudesse usar uma expressão regular, poderia usá-la ^[a-zA-Z0-9]*$para corresponder caracteres alfanuméricos em uma sequência.

Where       Value like '^[a-zA-Z0-9]*$'

Existe um equivalente SQL para isso?

Conheço esse tipo de coisa que pode ser feito no RegEx, mas preciso no T-SQL, não consigo carregar nenhum assembly personalizado neste servidor, portanto não posso usar expressões regulares.

A coluna real é varchar (200) . O agrupamento é Latin1_General_CI_AS. Estou usando o SQL Server 2012 Standard Edition.

pix1985
fonte
Vamos continuar esta discussão no chat .
Solomon Rutzky

Respostas:

12

A maior dificuldade em chegar a uma solução precisa é definir exatamente quais caracteres devem ser incluídos (ou excluídos, qualquer que seja a direção que faça mais sentido para a operação). Significado:

  • Estamos falando de VARCHARdados / ASCII ou NVARCHAR/ dados Unicode? A lista de caracteres de pontuação para dados ASCII depende da página de código, que por sua vez depende do agrupamento. ( nesta questão, estamos lidando com dados ASCII ).
  • Estamos lidando com pesquisas com distinção entre maiúsculas e minúsculas?
  • Para que agrupamento a coluna está definida? O Collation nos dirá a Página de Código e a distinção entre maiúsculas e minúsculas. ( nesta questão estamos lidandoLatin1_General_CI_AS )
  • é o termo "pontuação" para significar apenas caracteres de pontuação padrão (por exemplo ., ,, ;, :, etc) ou significa caracteres não alfanuméricos?
  • Os caracteres de espaço em branco estão incluídos?
  • Os caracteres de controle estão incluídos?
  • E sobre símbolos de moeda, como ¢, £, ¥, etc?
  • E quanto a símbolos como ©e ?
  • Quais caracteres são considerados "alfa"? São caracteres não-inglês, como Â, É, Ñ, ß, Þincluído?
  • Como esta pergunta trata dos teclados do Reino Unido (consulte a discussão para esta pergunta), e o caractere Æ/ æ?

Para ajudar a facilitar a clareza em relação ao comportamento esperado, a consulta a seguir mostrará todos os 256 caracteres do conjunto de caracteres Latin1 (ou seja, Página de Código 1252) e como duas variações da solução proposta de @ Shaneis operam. O primeiro campo (rotulado como Latin1_General_CI_AS) mostra a LIKEcláusula proposta por @Shaneis (no momento em que este artigo foi escrito) e o segundo campo (rotulado como Latin1_General_100_BIN2) mostra uma modificação na qual substituí o Collation para especificar um binário (ou seja, um Collation terminando em _BIN2; _BINOs agrupamentos foram descontinuados, portanto, não os use se você tiver acesso às _BIN2versões), o que significava que eu também precisava adicionar o A-Zintervalo para filtrar as letras maiúsculas, pois o agrupamento atual não diferencia maiúsculas de minúsculas:

;WITH nums AS
(
  SELECT TOP (256) (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1) AS [Decimal]
  FROM   [master].[sys].[all_objects]
)
SELECT nm.[Decimal],
       CHAR(nm.[Decimal]) AS [Character],
       CASE WHEN CHAR(nm.[Decimal]) LIKE '%[^a-z0-9]%'
               THEN 'x' ELSE '' END AS [Latin1_General_CI_AS],
       CASE WHEN CHAR(nm.[Decimal]) LIKE '%[^a-z0-9A-Z]%' COLLATE Latin1_General_100_BIN2
               THEN 'x' ELSE '' END AS [Latin1_General_100_BIN2]
FROM   nums nm;

ATUALIZAR

Deve-se mencionar que, se alguém estiver realmente procurando encontrar caracteres classificados como "pontuação" (e não "símbolo de moeda", "símbolo matemático" etc.), e se não for proibido usar o SQLCLR / carregar um costume Assembly (SQLCLR foi introduzido com o SQL Server 2005, e ainda preciso encontrar um bom motivo para não permitir, especialmente porque o Banco de Dados SQL do Azure V12 oferece suporte a SAFEAssemblies), então você pode usar Expressões Regulares, mas não pelo motivo que a maioria das pessoas adivinharia.

Em vez de usar Expressões regulares para criar um intervalo de caracteres mais funcional, ou mesmo em vez de usar algo como \w(ou seja, qualquer caractere "palavra"), você pode especificar a categoria Unicode dos caracteres que deseja filtrar e existem várias categorias definidas :

https://www.regular-expressions.info/unicode.html#category

Você pode até especificar o bloco Unicode para filtrar, como "InBengali" ou "InDingbats" ou "InOptical_Character_Recognition", etc:

https://www.regular-expressions.info/unicode.html#block

Existem inúmeros exemplos de criação de funções RegEx para SQL Server (embora a maioria dos exemplos não siga as práticas recomendadas de SQLCLR), ou você pode baixar a versão gratuita da biblioteca SQL # (que eu criei) e usar a função escalar RegEx_IsMatch da seguinte maneira :

SQL#.RegEx_IsMatch(Unicode-String-Expression, N'\p{P}', 1, NULL)

A \p{P}expressão significa \p= Categoria Unicode e {P}= toda pontuação (em oposição a um tipo específico de pontuação, como "Pontuação do Conector"). E a categoria "Pontuação" inclui todas as pontuações em todos os idiomas! Você pode ver a lista completa no site Unicode.org através do seguinte link (atualmente existem 717 Code Points nessa categoria):

http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AGeneral_Category%3DPunctuation%3A%5D

Uma versão atualizada da consulta de teste mostrado acima, incluindo um outro campo que usa SQL # .RegEx_IsMatch com \p{P}, e os resultados de todos os 3 testes em todos os 256 caracteres de página de código 1252 (ou seja Latin1_General) foi postado no PasteBin.com em:

Consulta T-SQL e resultados para filtrar tipos de caracteres


ATUALIZAÇÃO
O seguinte foi mencionado na discussão relacionada:

Você fez uma boa observação sobre caracteres acentuados; como eles são nomes de hotéis de todo o mundo, haverá caracteres acentuados nos nomes. Para o meu problema, eu gostaria de classificá-los como caracteres alfa válidos.

Nesse caso:

  1. Existem 11 caracteres que não estão em inglês que estão incluídos no conjunto de caracteres Latin1 / Página de códigos que não são correspondidos pelo a-zintervalo. Eles são: ð Ð Þ þ œ Œ š Š ž Ž Ÿ. Eles precisam ser adicionados ao curinga e, embora não sejam necessários no momento, não faria mal adicionar, A-Zpara que o padrão funcione tão bem em um agrupamento que diferencia maiúsculas de minúsculas. O resultado final é:
    LIKE '%[^a-zA-Z0-9ðÐÞþœŒšŠžŽŸ]%'

  2. Considerando-se que estes dados podem incluir "nomes de hotéis de todo o mundo", eu altamente recomendo mudar o tipo de dados da coluna a ser NVARCHARde modo que você pode armazenar todos os caracteres de todas as línguas. Manter isso como VARCHARcorre um risco muito alto de, eventualmente, ter perda de dados, pois você só pode representar os idiomas baseados em latim, e nem mesmo totalmente para aqueles que recebem as seis categorias adicionais de Unicode que fornecem caracteres adicionais relacionados a latim.

Solomon Rutzky
fonte
5

Posso simplificar um pouco demais isso, mas, se dissermos que a pontuação é o que resta quando os valores alfanuméricos são removidos, o seguinte procurará por seqüências de caracteres que contenham caracteres não alfanuméricos.

Create Table #Test
(
Value   VarChar(10)
) 
Insert Into #Test
Values ('123a'), ('456b'), ('12ABC'),('AB!23'),('C?D789')

-- Original
Select      *
From        #Test
Where       Value like '[0-9][0-9][0-9][a-z]'

-- Non Alpha-numeric
SELECT * FROM #Test WHERE Value LIKE '%[^a-z0-9]%';

DROP TABLE #Test;
Shaneis
fonte