J'utilise des scripts d'un autre sujet, mais la réponse acceptée ne fonctionne pas pour tous mes scénarios de données. J'aurais posé ma question sur l'original Comment vérifier les caractères non-ascii post, mais je n'ai pas encore assez de réputation pour commenter ou voter pour.
Questions:
Mes tests
J'ai créé SQL Fiddle avec des exemples de données, la procédure stockée à partir d'une des réponses et des requêtes pour illustrer le problème.
Requête 1: sample_table
-- Note: The "bad dash" row has char(150)
SELECT * FROM sample_table;
+-------------------+
| DataColumn |
+-------------------+
| test - good dash |
| test – bad dash |
+-------------------+
Requête 2: autre réponse par John montre la ligne "mauvais tiret" contenant char (150):
SELECT dbo.Find_Invalid_Chars(DataColumn) [Invalid Characters]
FROM sample_table
WHERE dbo.Find_Invalid_Chars(DataColumn) IS NOT NULL;
+----------------------+
| Invalid Characters |
+----------------------+
| test [150] bad dash |
+----------------------+
Requête 3: Le réponse acceptée par Martin Smith ne renvoie aucun résultat :
SELECT DataColumn AS [Bad Data]
FROM sample_table
WHERE DataColumn LIKE '%[' + CHAR(127)+ '-' +CHAR(255)+']%' COLLATE Latin1_General_100_BIN2;
+------------+
| [Bad Data] |
+------------+
-- No rows returned.
Conclusion
Malheureusement, j'ai souvent besoin de trouver des caractères dans (ou en dehors) d'une plage dans des bases de données dans lesquelles je ne peux pas créer de procédures stockées. J'aimerais vraiment trouver un correctif pour le réponse acceptée ou un simple script qui ne nécessiterait la création d'aucun objet (y compris les tables temporaires).
Aucune suggestion? Merci d'avance.
EDIT 1: La solution ne peut pas modifier ou ajouter des objets ou des paramètres dans la base de données. Je recherche une requête autonome qui sélectionnera des lignes avec un ou plusieurs caractères dans une plage comprise entre deux nombres CHAR()
, quels que soient les ASCII ou Extended ASCII numéro fourni.
EDIT 2: La DataColumn peut être dans VARCHAR
ou NVARCHAR
. Je n'ai aucun contrôle sur cela, donc j'espère trouver une requête autonome qui fonctionne pour les deux. Le but de la requête est de trouver dans la table/colonne source des caractères qui ne sont pas traités correctement par certaines applications logicielles. Les applications interprètent correctement la source, mais ont parfois des problèmes avec les caractères en dehors des plages "standard", bien que les plages varient selon l'application.
Pourquoi la réponse acceptée ne fonctionne-t-elle pas pour char (150)?
En fait, c'est le cas. Le problème est que votre test est mauvais/invalide. Vous testez la colonne, DataColumn
, utilise NVARCHAR
au lieu de VARCHAR
. Le caractère lui-même fonctionne dans les deux types de données, mais le comportement est différent en raison de la façon dont il est utilisé dans chaque cas:
Find_Invalid_Chars()
(c'est-à-dire la réponse "autre"), la chaîne est reconvertie en VARCHAR
car il s'agit du type de données du paramètre d'entrée pour cette fonction. Dans ce cas, cela fonctionne comme prévu (même si je pense que cela peut être fait beaucoup plus efficacement que cette boucle, mais c'est pour une autre fois ;-)LIKE
(c'est-à-dire la réponse "acceptée"), le résultat développé et concaténé de '%[' + CHAR(127)+ '-' +CHAR(255)+']%'
est en fait converti en NVARCHAR
puisque c'est le type de données de la colonne qu'il est comparé à (et NVARCHAR
a une priorité de type de données plus élevée), donc que la fonction LIKE
est pas se comportant comme attendu: soit le caractère CHAR(255)
correspond à un point de code différent, et/ou le caractère CHAR(150)
dans la colonne elle-même correspond à un point de code différent (le CHAR(127)
le caractère ne change pas car il se trouve dans la plage standard ASCII). Dans les deux cas, la conversion en NVARCHAR
entraîne la valeur numérique du caractère "En Dash" ("- ") pour ne plus être dans cette plage. Signification, la fonction LIKE
recherche des valeurs, y
, entre 127
et x
(où x
> = 128) et y
pour le caractère "En Dash" est maintenant> x
. Alors qu'en VARCHAR
, x
= 255 et y
= 150.La solution rapide pour voir que cela fonctionne est simplement de changer le type de données NVARCHAR
de la colonne DataColumn
en VARCHAR
(oui, il suffit de supprimer le "N" initial), puis recréez le schéma, puis exécutez, et la requête LIKE
se comportera comme prévu.
Les éléments suivants peuvent aider à expliquer pourquoi la création de la colonne de test NVARCHAR
a fait que la requête LIKE
ne correspond pas à la ligne:
SELECT UNICODE(CHAR(127)) AS [CHAR(127)],
UNICODE(CHAR(150)) AS [CHAR(150)],
UNICODE(CHAR(255)) AS [CHAR(255)];
/*
CHAR(127) CHAR(150) CHAR(255)
127 8211 255
*/
Comme vous pouvez le voir dans les résultats sous la requête, le "mauvais tiret", qui était CHAR(150)
est devenu NCHAR(8211)
lorsqu'il est stocké dans la colonne NVARCHAR
. Et, puisque ce prédicat utilise un classement binaire (généralement la bonne chose à faire dans ce scénario), il regardait les points/valeurs de code, pas les caractères. Par conséquent, la clause LIKE
recherchait des caractères avec des valeurs comprises entre 127 et 255, et 8211 n'est généralement pas dans cette plage ;-).
PS Veuillez garder à l'esprit que la fonction CHAR(150)
peut retourner des caractères différents, ou même NULL
, basé sur le classement par défaut de la base de données dans laquelle vous exécutez cette fonction. Cela est dû au fait que les données VARCHAR
sont basées sur des pages de codes, et celles-ci sont déterminées par le classement, et le classement utilisé lors de l'exécution de la fonction CHAR()
est le classement par défaut de la base de données active/actuelle . Cela affecte les valeurs 128 à 255. Les valeurs 0 à 127 renvoient toujours les mêmes caractères, quel que soit le classement, car il s'agit du jeu de caractères standard ASCII et sont les mêmes sur toutes les pages de codes prises en charge dans SQL Server (mais pas dans toutes les pages de codes en général).
PPS AUSSI, je viens de remarquer une légère différence de logique entre la fonction et la requête (c'est-à-dire les deux réponses de la question liée): CHAR(127)
est considérée comme bonne/valide dans le Find_Invalid_Chars()
, mais elle est considérée comme incorrecte/invalide dans la requête LIKE
. Si c'était moi, je considérerais CHAR(127)
valide car il fait partie du jeu de caractères standard ASCII. Mais, vous devez décider de ce que vous considérez. Soyez juste conscient de cette différence au cas où vous auriez besoin d'ajuster un peu la syntaxe LIKE
.
Donné:
Le but de la requête est de trouver dans la table/colonne source des caractères qui ne sont pas traités correctement par certaines applications logicielles.
et:
Les données peuvent être dans VARCHAR ou NVARCHAR.
Je dirais que:
Vous ne voulez pas convertir NVARCHAR
les données source en VARCHAR
car il pourrait y avoir des mappages "les mieux adaptés" qui traduire des caractères source non valides en caractères valides, mais une ou plusieurs de vos applications logicielles peuvent ne pas utiliser les mappages "les mieux adaptés".
SELECT NCHAR(178) AS [Unicode], -- Superscript 2 (U+00B2)
CONVERT(VARCHAR(5), NCHAR(178)
COLLATE SQL_Latin1_General_CP1_CI_AS) AS [CodePage-1252],
CONVERT(VARCHAR(5), NCHAR(178)
COLLATE Turkmen_100_CI_AS) AS [CodePage-1250]
/*
Unicode CodePage-1252 CodePage-1250
² ² 2
*/
NVARCHAR
qui contient beaucoup plus de 256 caractères.VARCHAR
et une pour NVARCHAR
.Cela étant dit:
La requête suivante renvoie des lignes contenant au moins un caractère qui n'est pas compris entre 0 et 127, pour VARCHAR
et NVARCHAR
. Mais, cela ne fonctionne qu'avec les colonnes NVARCHAR
pour les valeurs supérieures à 127.
SELECT *
FROM (VALUES (NCHAR(178)), (NCHAR(8211)), (N''), (NULL), (N'xy' + NCHAR(165)),
(N'AA'), (N'mM' + NCHAR(999) + N'Nn'), (N'#!~()')) tmp(TestValue)
WHERE tmp.[TestValue] LIKE N'%[^' + NCHAR(0) + N'-' + NCHAR(127)
+ N']%' COLLATE Latin1_General_100_BIN2;
/*
TestValue
²
–
xy¥
mMϧNn
*/
La requête suivante retourne également des lignes contenant au moins un caractère qui n'est pas compris entre 0 et 127, mais ne fonctionne que pour VARCHAR
Colonnes. Cependant, il permet d'utiliser des valeurs comprises entre 128 et 255.
SELECT *
FROM (VALUES (CHAR(178)), (CHAR(150)), (''), (NULL), ('AA'), ('#!~()'),
('xy' + CONVERT(VARCHAR(5), NCHAR(165) COLLATE Latin1_General_100_BIN2)),
('mM' + CONVERT(VARCHAR(5), NCHAR(199) COLLATE Latin1_General_100_BIN2) + 'Nn')
) tmp(TestValue)
WHERE tmp.[TestValue] LIKE '%[^' + CHAR(0) + '-' + CHAR(127)
+ ']%' COLLATE Latin1_General_100_BIN2;
/*
TestValue
²
–
xy¥
mMÇNn
*/
En ce qui concerne:
Les applications interprètent correctement la source, mais ont parfois des problèmes avec les caractères en dehors des plages "standard", bien que les plages varient selon l'application.