¿Existe un equivalente T-SQL para la puntuación ya que [0-9] es para números y [az] es para letras?

8

¿Existe un equivalente T-SQL de los patrones [0-9]y [a-z]que me permita extraer valores de una columna que contiene puntuación?

Por ejemplo:

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]'

Esto devolvería valores donde los primeros 3 caracteres son números entre 0 y 9 y el último carácter será una letra entre a y z, por lo que devolvería cosas como 123ay 456bpero no devolvería un valor de 12ABC.

Quiero saber si hay un equivalente para la puntuación como lo [0-9]es para los números y [a-z]para las letras para que regrese AB!23y C?D789.

Si pudiera usar una expresión regular, podría usar la expresión ^[a-zA-Z0-9]*$para unir caracteres alfanuméricos en una cadena.

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

¿Hay un equivalente SQL para esto?

Sé este tipo de cosas que se pueden hacer en RegEx pero lo necesito en T-SQL, no puedo cargar ningún ensamblado personalizado en este servidor, así que no puedo usar expresiones regulares.

La columna real es varchar (200) . La clasificación es Latin1_General_CI_AS. Estoy usando SQL Server 2012 Standard Edition.

pix1985
fuente
Continuemos esta discusión en el chat .
Solomon Rutzky

Respuestas:

12

La mayor dificultad para llegar a una solución precisa es definir exactamente qué caracteres se incluirán (o excluirán, en cualquier dirección que tenga más sentido para la operación). Sentido:

  • ¿Estamos hablando de VARCHARdatos / ASCII o NVARCHARdatos Unicode? La lista de caracteres de puntuación para los datos ASCII depende de la página de códigos, que a su vez depende de la clasificación. ( en esta pregunta estamos tratando con datos ASCII ).
  • ¿Estamos tratando con búsquedas sensibles a mayúsculas o minúsculas?
  • ¿En qué clasificación está configurada la columna? La clasificación nos dirá tanto la página de códigos como la distinción entre mayúsculas y minúsculas. ( en esta pregunta estamos tratandoLatin1_General_CI_AS )
  • es el término "puntuacion" para significar sólo los caracteres de puntuación estándar (por ejemplo ., ,, ;, :, etc) o significa caracteres no alfanuméricos?
  • ¿Están incluidos los caracteres de espacio en blanco?
  • ¿Están incluidos los personajes de control?
  • ¿Qué pasa con los símbolos de moneda como ¢, £, ¥, etc?
  • ¿Qué pasa con símbolos como ©y ?
  • ¿Qué caracteres se consideran "alfa"? Se caracteres no ingleses como Â, É, Ñ, ß, Þincluidos?
  • Dado que esta pregunta trata sobre los teclados del Reino Unido (consulte la discusión de esta pregunta), ¿qué pasa con el carácter Æ/ æ?

Para ayudar a facilitar la claridad con respecto al comportamiento esperado, la siguiente consulta mostrará los 256 caracteres del conjunto de caracteres Latin1 (es decir, la página de códigos 1252) y cómo funcionan dos variaciones de la solución propuesta de @ Shaneis . El primer campo (etiquetado como Latin1_General_CI_AS) muestra la LIKEcláusula propuesta por @Shaneis (a partir de este escrito) y el segundo campo (etiquetado como Latin1_General_100_BIN2) muestra una modificación donde anulé la Clasificación para especificar una binaria (es decir, una Clasificación que termina en _BIN2; el _BINLas intercalaciones están en desuso, así que no las use si tiene acceso a las _BIN2versiones), lo que significa que también necesitaba agregar el A-Zrango para filtrar las letras mayúsculas, ya que la intercalación actual no distingue entre mayúsculas y 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;

ACTUALIZAR

Cabe mencionar que SI uno está realmente tratando de encontrar personajes que se clasifican como "puntuacion" (y no "símbolo de la moneda", "símbolo matemático", etc), y SI no tiene prohibido utilizar SQLCLR / cargar una costumbre Ensamblaje (SQLCLR se introdujo con SQL Server 2005, y todavía no he encontrado una buena razón para no permitirlo, especialmente dado que Azure SQL Database V12 es compatible con SAFEensamblajes), entonces puede usar Expresiones regulares, pero no por la razón por la que la mayoría de las personas adivinaría

En lugar de usar Expresiones regulares para construir un rango de caracteres más funcional, o incluso en lugar de usar algo como \w(es decir, cualquier carácter de "palabra"), puede especificar la Categoría Unicode de los caracteres que desea filtrar, y hay varias categorías definidas :

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

Incluso puede especificar el bloque Unicode para filtrar, como "InBengali" o "InDingbats" o "InOptical_Character_Recognition", etc.

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

Existen numerosos ejemplos de creación de funciones RegEx para SQL Server (aunque la mayoría de los ejemplos no siguen las mejores prácticas de SQLCLR), o puede descargar la versión gratuita de la biblioteca SQL # (que creé) y usar la función escalar RegEx_IsMatch de la siguiente manera :

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

La \p{P}expresión significa \p= Categoría Unicode y {P}= toda la puntuación (en oposición a un tipo específico de puntuación, como "Puntuación de conector"). Y, la categoría "Puntuación" incluye todos los signos de puntuación en todos los idiomas. Puede ver la lista completa en el sitio Unicode.org a través del siguiente enlace (actualmente hay 717 puntos de código en esa categoría):

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

Una versión actualizada de la consulta de prueba que se muestra arriba, incluido otro campo que usa SQL # .RegEx_IsMatch con \p{P}, y los resultados de las 3 pruebas en los 256 caracteres de la página de códigos 1252 (es decir, Latin1_General) se ha publicado en PasteBin.com en:

Consulta T-SQL y resultados para filtrar tipos de caracteres


ACTUALIZACIÓN
Se mencionó lo siguiente en la discusión relacionada:

Has hecho un buen comentario sobre los caracteres acentuados, ya que son nombres de hoteles de todo el mundo habrá caracteres acentuados en los nombres, para mi problema, me gustaría clasificarlos como caracteres alfa válidos.

En este caso:

  1. Hay 11 caracteres que no están en inglés que se incluyen en el conjunto de caracteres Latin1 / Página de códigos que no coinciden con el a-zrango. Ellos son: ð Ð Þ þ œ Œ š Š ž Ž Ÿ. Estos deben agregarse al comodín y, aunque no son necesarios en este momento, no estaría de más agregarlos A-Zpara que el patrón funcione igual de bien en una clasificación sensible a mayúsculas y minúsculas. El resultado final es:
    LIKE '%[^a-zA-Z0-9ðÐÞþœŒšŠžŽŸ]%'

  2. Teniendo en cuenta que estos datos pueden incluir "nombres de hoteles de todo el mundo", me altamente recomiendan cambiar el tipo de datos de la columna que se va NVARCHARpara que pueda almacenar todos los caracteres de todos los idiomas. Mantener esto como VARCHARun riesgo muy alto de eventualmente tener pérdida de datos ya que solo puede representar los idiomas basados ​​en el latín, y ni siquiera completamente para aquellos que reciben las seis categorías Unicode suplementarias que proporcionan caracteres adicionales relacionados con el latín.

Solomon Rutzky
fuente
5

Puede que esté simplificando demasiado esto un poco, pero, si decimos que la puntuación es todo lo que queda cuando se eliminan los valores alfanuméricos, entonces lo siguiente buscará cadenas que tengan caracteres no 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
fuente