J'essaie d'écrire une requête qui remplace les caractères spéciaux par de l'espace. Le code ci-dessous permet d'identifier les lignes. (les caractères alphanumériques, la virgule et l'espace sont valides):
SELECT columnA
FROM tableA
WHERE columnA like '%[^a-Z0-9, ]%'
Comment puis-je intégrer la fonction replace dans l'instruction select afin que tous les caractères autres que alphanumériques, virgule et espace dans le jeu de résultats soient remplacés par '' (espace). Celui-ci ne fonctionnera pas:
SELECT replace(columnA,'%[^a-Z0-9, ]%',' ')
FROM tableA
WHERE columnA like '%[^a-Z0-9, ]%'
Si vous êtes assuré de n'utiliser que les 26 lettres de l'alphabet anglais américain (versions majuscules et minuscules), alors vous pouvez vous en sortir en utilisant LIKE
et/ou PATINDEX
avec la notation de plage simple de [a-z]
(vous n'avez pas besoin d'utiliser un "Z" majuscule lorsque vous utilisez un classement insensible à la casse).
Mais, si vous pouvez obtenir des caractères non trouvés dans l'alphabet en-US mais disponibles dans diverses pages de code/classements pour les données VARCHAR
(par exemple Þ
= Majuscule latine "Thorn" = SELECT CHAR(0xDE)
), vous devrez peut-être les inclure dans la classe de caractères: [a-z0-9, Þ]
. Bien sûr, quels seraient ces caractères supplémentaires sur une base par page de codes.
Sachez également que le type de classement (SQL Server vs Windows) et les paramètres de sensibilité (casse, accent, etc. sensible vs insensible) affecteront les caractères inclus dans une plage particulière. Par exemple, les classements SQL Server trient les lettres majuscules et minuscules dans l'ordre opposé aux classements Windows. Autrement dit, en supposant un classement sensible à la casse pour les deux types de classement, l'un fera AaBb...
Et l'autre fera aAbB...
. L'effet sera que a
sera dans la plage de A-Z
Pour l'un d'entre eux, mais pas pour l'autre. Et la plage de a-Z
Ne correspondra à aucun caractère dans un classement binaire (l'un se terminant par _BIN
Ou _BIN2
, Mais n'utilisez pas _BIN
) étant donné que la valeur de A
est 65 et a
est 97, il s'agit donc d'une plage non valide de 97 à 65 ;-). Il y a beaucoup trop de variations pour donner des exemples ici, donc j'essaierai de publier une explication détaillée sur mon blog très bientôt (puis je mettrai à jour cela avec le lien). Cependant, si vous allez être strict en acceptant uniquement les caractères anglais américain (même si vous pouvez obtenir des lettres valides dans d'autres langues), votre meilleure option sera probablement d'utiliser le modèle suivant et Classement:
LIKE '%[^A-Za-z0-9, ]%' COLLATE Latin1_General_100_BIN2
Maintenant, si vous prenez en charge les données NVARCHAR
et pouvez obtenir des caractères "Word" dans différentes langues, alors T-SQL ne sera pas d'une grande utilité car il n'a aucun moyen réel de différencier ces choses. Dans ce cas, vous devez utiliser une expression régulière (RegEx) - en particulier la méthode/fonction Replace
- et celles-ci ne sont disponibles que via SQLCLR. Ce qui suit montre un exemple de remplacement de plusieurs caractères "spéciaux", tout en laissant toutes les lettres valides dans au moins une langue:
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);
Retour:
this is a TEST, to strip out ç ƒ special ij ೫ chars 舛 დ א B
L'expression RegEx signifie:
\W
= Un "échappement" RegEx signifiant "tout non - caractère Word"\p{Pc}
= Une "catégorie" Unicode de "Ponctuation, connecteur" (nécessaire uniquement pour la correspondance car cette "catégorie" est spécifiquement exclue par l'échappement \W
)-[,]
= Soustraction de classe (cela est nécessaire pour exclure les virgules de la correspondance en tant que "spécial" car elles sont incluses dans l'échappement \W
)Vous pouvez faire une mise à jour d'une table simplement en émettant:
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;
Veuillez noter que pour ces exemples, j'ai utilisé deux fonctions disponibles dans la bibliothèque Free version SQL # des fonctions SQLCLR, que j'ai créée (mais encore une fois, elles sont gratuites). Notez également que j'ai utilisé les versions "4k" qui sont plus rapides en raison de l'utilisation des types de paramètres NVARCHAR(4000)
au lieu de NVARCHAR(MAX)
. Si vos données utilisent NVARCHAR(MAX)
, supprimez simplement le "4k" des noms de fonction.
Veuillez également voir:
J'ai un article ici qui fait quelque chose de similaire .
Fondamentalement, j'utilise un CTE récursif pour aller en boucle encore et encore en remplaçant un "mauvais" caractère à la fois. J'utilise STUFF pour supprimer 1 caractère (bien que vous puissiez l'utiliser pour le remplacer par un espace) et PATINDEX pour trouver l'emplacement du caractère que je souhaite supprimer. Vous pouvez le modifier légèrement pour faire ce que vous cherchez. Cependant, il crée une "bonne" liste, il ne met pas à jour la liste existante.
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);
Vous devriez pouvoir modifier la partie inférieure pour faire une mise à jour plutôt qu'une simple requête, mais je ne l'ai pas essayé. Je suis presque sûr que cela ressemblerait à quelque chose comme ceci:
UPDATE FixBadChars
SET StringToFix = FixedString
WHERE MyCounter =
(SELECT MAX(MyCounter)
FROM FixBadChars Fixed
WHERE Fixed.Id = FixBadChars.Id)
OPTION (MAXRECURSION 1000);
En ce qui concerne l'évolutivité, j'ai renvoyé environ 170 000 lignes nettoyées en moins de 30 secondes. Encore une fois, je ne suis pas sûr de faire une mise à jour, mais c'était sur mon ordinateur portable qui est assez lent avec seulement 6 Go de RAM.
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