Comment renvoyer des lignes avec des caractères non ASCII à l'aide de SQL Server?
Si vous pouvez montrer comment le faire pour une colonne, ce serait bien.
Je fais quelque chose comme ça maintenant, mais ça ne marche pas
select *
from Staging.APARMRE1 as ar
where ar.Line like '%[^!-~ ]%'
Pour un crédit supplémentaire, s'il peut s'étendre sur toutes les varchar
colonnes d'un tableau, ce serait exceptionnel! Dans cette solution, ce serait bien de renvoyer trois colonnes:
Id | FieldName | InvalidText |
----+-----------+-------------------+
25 | LastName | Solís |
56 | FirstName | François |
100 | Address1 | 123 Ümlaut street |
Les caractères non valides seraient en dehors de la plage de SPACE (32dix) à travers ~
(127dix)
Voici une solution pour la recherche sur une seule colonne à l'aide de PATINDEX.
Il affiche également StartPosition, InvalidCharacter et ASCII code.
select line,
patindex('%[^ !-~]%' COLLATE Latin1_General_BIN,Line) as [Position],
substring(line,patindex('%[^ !-~]%' COLLATE Latin1_General_BIN,Line),1) as [InvalidCharacter],
ascii(substring(line,patindex('%[^ !-~]%' COLLATE Latin1_General_BIN,Line),1)) as [ASCIICode]
from staging.APARMRE1
where patindex('%[^ !-~]%' COLLATE Latin1_General_BIN,Line) >0
Ce script recherche les caractères non ascii dans une colonne. Il génère une chaîne de tous les caractères valides, ici les points de code 32 à 127. Ensuite, il recherche les lignes qui ne correspondent pas à la liste:
declare @str varchar(128)
declare @i int
set @str = ''
set @i = 32
while @i <= 127
begin
set @str = @str + '|' + char(@i)
set @i = @i + 1
end
select col1
from YourTable
where col1 like '%[^' + @str + ']%' escape '|'
J'ai exécuté ce morceau de code avec succès
declare @UnicodeData table (
data nvarchar(500)
)
insert into
@UnicodeData
values
(N'Horse�')
,(N'Dog')
,(N'Cat')
select
data
from
@UnicodeData
where
data collate LATIN1_GENERAL_BIN != cast(data as varchar(max))
Ce qui fonctionne bien pour les colonnes connues.
Pour un crédit supplémentaire, j'ai écrit ce script rapide pour rechercher toutes les colonnes nvarchar dans une table donnée pour les caractères Unicode.
declare
@sql varchar(max) = ''
,@table sysname = 'mytable' -- enter your table here
;with ColumnData as (
select
RowId = row_number() over (order by c.COLUMN_NAME)
,c.COLUMN_NAME
,ColumnName = '[' + c.COLUMN_NAME + ']'
,TableName = '[' + c.TABLE_SCHEMA + '].[' + c.TABLE_NAME + ']'
from
INFORMATION_SCHEMA.COLUMNS c
where
c.DATA_TYPE = 'nvarchar'
and c.TABLE_NAME = @table
)
select
@sql = @sql + 'select FieldName = ''' + c.ColumnName + ''', InvalidCharacter = [' + c.COLUMN_NAME + '] from ' + c.TableName + ' where ' + c.ColumnName + ' collate LATIN1_GENERAL_BIN != cast(' + c.ColumnName + ' as varchar(max)) ' + case when c.RowId <> (select max(RowId) from ColumnData) then ' union all ' else '' end + char(13)
from
ColumnData c
-- check
-- print @sql
exec (@sql)
Je ne suis pas un fan de SQL dynamique, mais il a ses utilisations pour des requêtes exploratoires comme celle-ci.
exécutant les différentes solutions sur certaines données du monde réel - 12M lignes varchar longueur ~ 30, environ 9k lignes douteuses, pas d'index de texte intégral en jeu, la solution patIndex est la plus rapide, et elle sélectionne également le plus de lignes.
(km pré-exécuté pour régler le cache sur un état connu, a exécuté les 3 processus et a finalement exécuté à nouveau le km - les 2 derniers parcours de km ont donné des temps dans les 2 secondes)
solution patindex de Gerhard Weiss - Runtime 0:38, retourne 9144 lignes
select dodgyColumn from myTable fcc
WHERE patindex('%[^ !-~]%' COLLATE Latin1_General_BIN,dodgyColumn ) >0
la solution sous-chaîne-nombres par MT. - Runtime 1:16, a renvoyé 8996 lignes
select dodgyColumn from myTable fcc
INNER JOIN dbo.Numbers32k dn ON dn.number<(len(fcc.dodgyColumn ))
WHERE ASCII(SUBSTRING(fcc.dodgyColumn , dn.Number, 1))<32
OR ASCII(SUBSTRING(fcc.dodgyColumn , dn.Number, 1))>127
solution udf par Deon Robertson - Runtime 3:47, renvoie 7316 lignes
select dodgyColumn
from myTable
where dbo.udf_test_ContainsNonASCIIChars(dodgyColumn , 1) = 1
Une fonction définie par l'utilisateur est disponible sur le Web "Analyser alphanumérique". Google UDF analyse les caractères alphanumériques et vous devriez trouver le code correspondant. Cette fonction définie par l'utilisateur supprime tous les caractères qui ne se situent pas entre 0-9, a-z et A-Z.
Select * from Staging.APARMRE1 ar
where udf_parsealpha(ar.last_name) <> ar.last_name
Cela devrait ramener tous les enregistrements qui ont un nom de famille avec des caractères non valides pour vous ... bien que votre question sur les points bonus soit un peu plus difficile, mais je pense qu'une déclaration de cas pourrait le gérer. Ceci est un peu le code pseudo, je ne suis pas tout à fait sûr si cela fonctionnerait.
Select id, case when udf_parsealpha(ar.last_name) <> ar.last_name then 'last name'
when udf_parsealpha(ar.first_name) <> ar.first_name then 'first name'
when udf_parsealpha(ar.Address1) <> ar.last_name then 'Address1'
end,
case when udf_parsealpha(ar.last_name) <> ar.last_name then ar.last_name
when udf_parsealpha(ar.first_name) <> ar.first_name then ar.first_name
when udf_parsealpha(ar.Address1) <> ar.last_name then ar.Address1
end
from Staging.APARMRE1 ar
where udf_parsealpha(ar.last_name) <> ar.last_name or
udf_parsealpha(ar.first_name) <> ar.first_name or
udf_parsealpha(ar.Address1) <> ar.last_name
J'ai écrit ceci dans la boîte aux lettres du forum ... donc je ne sais pas trop si cela fonctionnera tel quel, mais cela devrait être proche. Je ne sais pas trop comment il se comportera si un seul enregistrement a deux champs avec des caractères non valides.
Vous pouvez également remplacer la clause from d'une table unique par une sous-requête qui ressemble à quelque chose comme:
select id,fieldname,value from (
Select id,'last_name' as 'fieldname', last_name as 'value'
from Staging.APARMRE1 ar
Union
Select id,'first_name' as 'fieldname', first_name as 'value'
from Staging.APARMRE1 ar
---(and repeat unions for each field)
)
where udf_parsealpha(value) <> value
L'avantage ici est que pour chaque colonne, vous n'aurez qu'à étendre la déclaration d'union ici, tandis que vous devez mettre cette comparaison trois fois pour chaque colonne dans la version de déclaration de cas de ce script
Pour trouver quel champ contient des caractères non valides:
SELECT * FROM Staging.APARMRE1 FOR XML AUTO, TYPE
Vous pouvez le tester avec cette requête:
SELECT top 1 'char 31: '+char(31)+' (hex 0x1F)' field
from sysobjects
FOR XML AUTO, TYPE
Le résultat sera:
Msg 6841, niveau 16, état 1, ligne 3 pour XML n'a pas pu sérialiser les données pour le "champ" de noeud car il contient un caractère (0x001F) qui n'est pas autorisé en XML. Pour récupérer ces données à l'aide de FOR XML, convertissez-les en type de données binaire, varbinaire ou image et utilisez la directive BINARY BASE64.
Il est très utile lorsque vous écrivez des fichiers xml et obtenez une erreur de caractères invalides lors de la validation.
Voici un UDF I construit pour détecter les colonnes avec des caractères ascii étendus. C'est rapide et vous pouvez étendre le jeu de caractères que vous souhaitez vérifier. Le deuxième paramètre vous permet de basculer entre la vérification de tout ce qui est en dehors du jeu de caractères standard ou l'autorisation d'un jeu étendu:
create function [dbo].[udf_ContainsNonASCIIChars]
(
@string nvarchar(4000),
@checkExtendedCharset bit
)
returns bit
as
begin
declare @pos int = 0;
declare @char varchar(1);
declare @return bit = 0;
while @pos < len(@string)
begin
select @char = substring(@string, @pos, 1)
if ascii(@char) < 32 or ascii(@char) > 126
begin
if @checkExtendedCharset = 1
begin
if ascii(@char) not in (9,124,130,138,142,146,150,154,158,160,170,176,180,181,183,184,185,186,192,193,194,195,196,197,199,200,201,202,203,204,205,206,207,209,210,211,212,213,214,216,217,218,219,220,221,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,248,249,250,251,252,253,254,255)
begin
select @return = 1;
select @pos = (len(@string) + 1)
end
else
begin
select @pos = @pos + 1
end
end
else
begin
select @return = 1;
select @pos = (len(@string) + 1)
end
end
else
begin
select @pos = @pos + 1
end
end
return @return;
end
USAGE:
select Address1
from PropertyFile_English
where udf_ContainsNonASCIIChars(Address1, 1) = 1