Existe-t-il un équivalent T-SQL pour la ponctuation comme [0-9] pour les nombres et [az] pour les lettres?

8

Existe-t-il un équivalent T-SQL des modèles [0-9]et [a-z]qui me permettra d'extraire des valeurs d'une colonne contenant de la ponctuation?

Par exemple:

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

Cela retournerait des valeurs où les 3 premiers caractères sont des nombres entre 0 et 9 et le dernier caractère sera une lettre entre a et z, donc retournerait des choses comme 123aet 456bmais ne retournerait pas une valeur de 12ABC.

Je veux savoir s'il existe un équivalent pour la ponctuation comme [0-9]pour les chiffres et [a-z]pour les lettres afin qu'il revienne AB!23et C?D789?

Si je pouvais utiliser une expression régulière, je pourrais utiliser l'expression ^[a-zA-Z0-9]*$pour faire correspondre des caractères alphanumériques dans une chaîne.

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

Existe-t-il un équivalent SQL pour cela?

Je sais ce genre de chose qui peut être fait dans RegEx mais j'en ai besoin dans T-SQL, je ne peux pas charger d'assemblys personnalisés sur ce serveur, donc je ne peux pas utiliser d'expressions régulières.

La vraie colonne est varchar (200) . Le classement est Latin1_General_CI_AS. J'utilise SQL Server 2012 Standard Edition.

pix1985
la source
Continuons cette discussion dans le chat .
Solomon Rutzky

Réponses:

12

La plus grande difficulté pour arriver à une solution précise est de définir exactement quels caractères doivent être inclus (ou exclus, selon la direction la plus logique pour l'opération). Sens:

  • Parlons-nous de VARCHARdonnées / ASCII ou de données NVARCHAR/ Unicode? La liste des caractères de ponctuation pour les données ASCII dépend de la page de codes qui à son tour dépend du classement. ( dans cette question, nous traitons des données ASCII ).
  • Avons-nous affaire à des recherches sensibles à la casse ou à la casse?
  • À quel classement la colonne est-elle définie? Le classement nous indiquera à la fois la page de codes et la sensibilité à la casse. ( dans cette question, nous traitonsLatin1_General_CI_AS )
  • est le terme « ponctuation » pour signifier seulement des caractères de ponctuation standard (par exemple ., ,, ;, :, etc.) ou que cela signifie des caractères non alphanumériques?
  • Les caractères d'espacement sont-ils inclus?
  • Les caractères de contrôle sont-ils inclus?
  • Qu'en est- il des symboles monétaires tels que ¢, £, ¥, etc?
  • Qu'en est-il des symboles tels que ©et ?
  • Quels caractères sont considérés comme "alpha"? Sont des caractères non-anglais tels que Â, É, Ñ, ß, Þcompris?
  • Puisque cette question concerne les claviers britanniques (voir la discussion pour cette question), qu'en est-il du caractère Æ/ æ?

Afin de faciliter la clarté concernant le comportement attendu, la requête suivante montrera les 256 caractères du jeu de caractères Latin1 (c'est-à-dire la page de code 1252) et comment deux variantes de la solution proposée par @ Shaneis fonctionnent. Le premier champ (étiqueté comme Latin1_General_CI_AS) montre la LIKEclause telle que proposée par @Shaneis (au moment de la rédaction de cet article) et le deuxième champ (étiqueté comme Latin1_General_100_BIN2) montre une modification dans laquelle j'ai outrepassé le classement pour en spécifier un binaire (c'est-à-dire un classement se terminant par _BIN2; le _BINLes classements sont obsolètes, ne les utilisez donc pas si vous avez accès aux _BIN2versions), ce qui signifiait que j'avais également besoin d'ajouter dans la A-Zplage pour filtrer les lettres majuscules car le classement actuel est insensible à la casse:

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

MISE À JOUR

Il convient de mentionner que SI on cherche vraiment à trouver des caractères qui sont classés comme étant de la «ponctuation» (et non pas du «symbole monétaire», du «symbole mathématique», etc.), et SI on n'est pas interdit d'utiliser SQLCLR / de charger une coutume Assemblage (SQLCLR a été introduit avec SQL Server 2005, et je n'ai pas encore trouvé de bonne raison de ne pas l'autoriser, d'autant plus qu'Azure SQL Database V12 prend en charge les SAFEassemblys), alors vous pouvez utiliser des expressions régulières, mais pas pour la raison pour laquelle la plupart des gens devinerait.

Plutôt que d'utiliser des expressions régulières pour créer une plage de caractères plus fonctionnelle, ou même plutôt que d'utiliser quelque chose comme \w(signifiant tout caractère "mot"), vous pouvez spécifier la catégorie Unicode des caractères que vous souhaitez filtrer, et il existe plusieurs catégories définies :

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

Vous pouvez même spécifier le bloc Unicode sur lequel filtrer, tel que "InBengali" ou "InDingbats" ou "InOptical_Character_Recognition", etc.:

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

Il existe de nombreux exemples de création de fonctions RegEx pour SQL Server (bien que la plupart des exemples ne suivent pas les meilleures pratiques SQLCLR), ou vous pouvez télécharger la version gratuite de la bibliothèque SQL # (que j'ai créée) et utiliser la fonction scalaire RegEx_IsMatch comme suit :

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

L' \p{P}expression signifie \p= catégorie Unicode et {P}= toute la ponctuation (par opposition à un type de ponctuation spécifique, tel que "ponctuation de connecteur"). ET, la catégorie "Ponctuation" inclut toute la ponctuation dans toutes les langues! Vous pouvez voir la liste complète sur le site Unicode.org via le lien suivant (il y a actuellement 717 Points de Code dans cette catégorie):

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

Une version mise à jour de la requête de test ci-dessus, y compris un autre champ qui utilise SQL # .RegEx_IsMatch avec \p{P}, et les résultats des 3 tests sur les 256 caractères de la page de code 1252 (c'est-à-dire Latin1_General) a été publiée sur PasteBin.com à l'adresse suivante:

Requête T-SQL et résultats pour filtrer les types de caractères


MISE À JOUR
Les éléments suivants ont été mentionnés dans la discussion connexe:

Vous avez fait une bonne remarque sur les caractères accentués, comme ce sont des noms d'hôtels du monde entier, il y aura des caractères accentués dans les noms, pour mon problème, je voudrais les classer comme des caractères alpha valides.

Dans ce cas:

  1. Il y a 11 caractères non anglais inclus dans le jeu de caractères Latin1 / la page de codes qui ne correspondent pas à la a-zplage. Ils sont: ð Ð Þ þ œ Œ š Š ž Ž Ÿ. Ceux-ci doivent être ajoutés au caractère générique, et bien que ce ne soit pas nécessaire pour le moment, cela ne ferait pas de mal à ajouter A-Zafin que le modèle fonctionne tout aussi bien sur un classement sensible à la casse. Le résultat final est:
    LIKE '%[^a-zA-Z0-9ðÐÞþœŒšŠžŽŸ]%'

  2. Étant donné que ces données peuvent inclure des "noms d'hôtels du monde entier", je recommanderais fortement de changer le type de données de la colonne NVARCHARafin que vous puissiez stocker tous les caractères de toutes les langues. Garder cela comme VARCHARun risque très élevé de perte de données, car vous ne pouvez représenter que les langues basées sur le latin, et même pas complètement pour celles qui reçoivent les six catégories Unicode supplémentaires qui fournissent des caractères latins supplémentaires.

Solomon Rutzky
la source
5

Je simplifie peut-être un peu trop cela, mais, si nous disons que la ponctuation est tout ce qui reste lorsque les valeurs alphanumériques sont supprimées, alors ce qui suit recherchera les chaînes qui contiennent des caractères non alphanumériques.

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
la source