Question

Comment les lignes avec des caractères non-ASCII sont retournés en utilisant SQL Server?
Si vous pouvez montrer comment le faire pour une colonne serait grande.

Je suis en train de faire quelque chose comme ça maintenant, mais il ne fonctionne pas

select *
from Staging.APARMRE1 as ar
where ar.Line like '%[^!-~ ]%'

Pour un crédit supplémentaire, si elle peut couvrir tous colonnes de varchar dans une table, ce serait exceptionnel! Dans cette solution, ce serait bien de revenir trois colonnes:

  • Le champ d'identité pour cet enregistrement. (Cela permettra à l'ensemble du dossier à examiner avec une autre requête.)
  • Le nom de colonne
  • Le texte avec le caractère non valide
 Id | FieldName | InvalidText       |
----+-----------+-------------------+
 25 | LastName  | Solís             |
 56 | FirstName | François          |
100 | Address1  | 123 Ümlaut street |

caractères incorrects serait tout en dehors de la gamme de l'espace (32 10 ) à travers ~ (127 10 )

Était-ce utile?

La solution

essayer quelque chose comme ceci:

DECLARE @YourTable table (PK int, col1 varchar(20), col2 varchar(20), col3 varchar(20))
INSERT @YourTable VALUES (1, 'ok','ok','ok')
INSERT @YourTable VALUES (2, 'BA'+char(182)+'D','ok','ok')
INSERT @YourTable VALUES (3, 'ok',char(182)+'BAD','ok')
INSERT @YourTable VALUES (4, 'ok','ok','B'+char(182)+'AD')
INSERT @YourTable VALUES (5, char(182)+'BAD','ok',char(182)+'BAD')
INSERT @YourTable VALUES (6, 'BAD'+char(182),'B'+char(182)+'AD','BAD'+char(182)+char(182)+char(182))

--if you have a Numbers table use that, other wise make one using a CTE
;WITH AllNumbers AS
(   SELECT 1 AS Number
    UNION ALL
    SELECT Number+1
        FROM AllNumbers
        WHERE Number<1000
)
SELECT 
    pk, 'Col1' BadValueColumn, CONVERT(varchar(20),col1) AS BadValue --make the XYZ in convert(varchar(XYZ), ...) the largest value of col1, col2, col3
    FROM @YourTable           y
        INNER JOIN AllNumbers n ON n.Number <= LEN(y.col1)
    WHERE ASCII(SUBSTRING(y.col1, n.Number, 1))<32 OR ASCII(SUBSTRING(y.col1, n.Number, 1))>127
UNION
SELECT 
    pk, 'Col2' BadValueColumn, CONVERT(varchar(20),col2) AS BadValue --make the XYZ in convert(varchar(XYZ), ...) the largest value of col1, col2, col3
    FROM @YourTable           y
        INNER JOIN AllNumbers n ON n.Number <= LEN(y.col2)
    WHERE ASCII(SUBSTRING(y.col2, n.Number, 1))<32 OR ASCII(SUBSTRING(y.col2, n.Number, 1))>127
UNION
SELECT 
    pk, 'Col3' BadValueColumn, CONVERT(varchar(20),col3) AS BadValue --make the XYZ in convert(varchar(XYZ), ...) the largest value of col1, col2, col3
    FROM @YourTable           y
        INNER JOIN AllNumbers n ON n.Number <= LEN(y.col3)
    WHERE ASCII(SUBSTRING(y.col3, n.Number, 1))<32 OR ASCII(SUBSTRING(y.col3, n.Number, 1))>127
order by 1
OPTION (MAXRECURSION 1000)

SORTIE:

pk          BadValueColumn BadValue
----------- -------------- --------------------
2           Col1           BA¶D
3           Col2           ¶BAD
4           Col3           B¶AD
5           Col1           ¶BAD
5           Col3           ¶BAD
6           Col1           BAD¶
6           Col2           B¶AD
6           Col3           BAD¶¶¶

(8 row(s) affected)

Autres conseils

Voici une solution pour la recherche de la colonne unique à l'aide PATINDEX.
Il affiche également le StartPosition, InvalidCharacter et code ASCII.

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 pour les caractères non-ascii dans une colonne. Il génère une chaîne de tous les caractères valides, ici point 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 '|'

Je dirige ce petit 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, je l'ai écrit ce script rapide pour rechercher toutes les colonnes nvarchar dans une table 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 fan de SQL dynamique, mais il a ses utilisations pour les requêtes exploratoires comme celui-ci.

l'exécution des différentes solutions sur des données réelles - lignes 12M longueur varchar ~ 30, environ 9k lignes louches, sans index en texte intégral en jeu, la solution patindex est le plus rapide, et il sélectionne également les lignes les plus.

(. Pré-ran km pour définir le cache à un état connu, a couru les 3 processus, et enfin a couru à nouveau km - les 2 dernières séries de km ont donné temps à moins de 2 secondes)

solution de patindex par Gerhard Weiss - Durée 00:38, 9144 renvoie lignes

select dodgyColumn from myTable fcc
WHERE  patindex('%[^ !-~]%' COLLATE Latin1_General_BIN,dodgyColumn ) >0

la solution sous-chaîne numéros par MT. - Runtime 01h16, retour 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

udf solution par Deon Robertson - Durée 03:47, 7316 renvoie lignes

select dodgyColumn 
from myTable 
where dbo.udf_test_ContainsNonASCIIChars(dodgyColumn , 1) = 1

Il y a une fonction définie par l'utilisateur disponible sur le web « Parse Alphanumeric ». Google UDF parse alphanumérique et vous devez trouver le code pour elle. Cette fonction définie par l'utilisateur supprime tous les caractères qui ne correspondent 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 Ramenez tous les dossiers qui ont un last_name avec les caractères non valides pour vous ... si vos points bonus question est un peu plus difficile, mais je pense une déclaration de cas pourrait le manipuler. Ceci est un pseudo-code binaire, je ne suis pas tout à fait sûr si elle travaillerait.

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 dans la rubrique post-forum ... donc je ne suis pas tout à fait sûr si cela va fonctionner comme est, mais il devrait être proche. Je ne suis pas sûr de savoir comment il se comportera si un enregistrement unique a deux champs avec les caractères non valides soit.

Comme alternative, vous devriez être en mesure de modifier la clause de loin d'une seule table et dans 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

avantage ici est pour chaque colonne que vous aurez seulement besoin d'étendre la déclaration du syndicat ici, alors que vous devez mettre cette comparisson trois fois pour chaque colonne dans la version de l'instruction cas de ce script

Voici une UDF I construit aux colonnes detectc avec charaters ascii étendues. Il est rapide et vous pouvez étendre le jeu de caractères que vous voulez vérifier. Le second paramètre vous permet de basculer entre les vérifier quoi que ce soit en dehors du jeu de caractères standard ou permettant un ensemble é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

Utilisation:

select Address1 
from PropertyFile_English
where udf_ContainsNonASCIIChars(Address1, 1) = 1

Pour quel champ contient des caractères incorrects:

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 FOR XML ne pouvait pas sérialiser la   données pour le nœud « champ », car il contient un caractère (0x001F) qui   ne sont pas autorisés en XML. Pour récupérer ces données en utilisant FOR XML, convertir   en binaire, varbinary ou de l'image type de données et utiliser le BINARY BASE64   directive.

Il est très utile lorsque vous écrivez des fichiers xml et obtenez erreur de caractères non valides lors de la valider.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top