web-dev-qa-db-fra.com

Comment puis-je trouver des caractères Unicode / non ASCII dans un champ NTEXT dans une table SQL Server 2005?

J'ai une table avec quelques milliers de lignes. Les champs de description et de résumé sont NTEXT et contiennent parfois des caractères non ASCII. Comment puis-je localiser toutes les lignes avec des caractères non ASCII?

32
TheSoftwareJedi

J'ai parfois utilisé cette instruction "cast" pour trouver des caractères "étranges"

select 
    *
from 
    <Table>
where 
    <Field> != cast(<Field> as varchar(1000))
49
CC1960

Créez d'abord une chaîne avec tous les caractères qui ne vous intéressent pas (l'exemple utilise la plage 0x20 - 0x7F, ou 7 bits sans les caractères de contrôle.) Chaque caractère est préfixé avec |, pour une utilisation ultérieure dans la clause d'échappement.

-- Start with tab, line feed, carriage return
declare @str varchar(1024)
set @str = '|' + char(9) + '|' + char(10) + '|' + char(13)

-- Add all normal ASCII characters (32 -> 127)
declare @i int
set @i = 32
while @i <= 127
    begin
    -- Uses | to escape, could be any character
    set @str = @str + '|' + char(@i)
    set @i = @i + 1
    end

L'extrait suivant recherche tout caractère qui ne figure pas dans la liste. Le% correspond à 0 ou plusieurs caractères. Le [] correspond à l'un des caractères à l'intérieur du [], par exemple [abc] correspondrait à a, b ou c. Le ^ annule la liste, par exemple [^ abc] correspondrait à tout ce qui n'est pas a, b ou c.

select *
from yourtable
where yourfield like '%[^' + @str + ']%' escape '|'

Le caractère d'échappement est obligatoire car sinon la recherche de caractères comme],% ou _ gâcherait l'expression LIKE.

J'espère que cela est utile et merci au commentaire de JohnFX sur l'autre réponse.

17
Andomar

Techniquement, je crois qu'un NCHAR (1) est un caractère valide ASCII IF & Only IF UNICODE (@NChar) <256 et ASCII (@NChar) = UNICODE (@NChar) bien que cela puisse pas exactement ce que vous vouliez. Par conséquent, ce serait une bonne solution:

;With cteNumbers as
(
    Select ROW_NUMBER() Over(Order By c1.object_id) as N
    From sys.system_columns c1, sys.system_columns c2
)
Select Distinct RowID
From YourTable t
    Join cteNumbers n ON n <= Len(CAST(TXT As NVarchar(MAX)))
Where UNICODE(Substring(TXT, n.N, 1)) > 255
    OR UNICODE(Substring(TXT, n.N, 1)) <> ASCII(Substring(TXT, n.N, 1))

Cela devrait également être très rapide.

3
RBarryYoung

Et voilà:

SELECT *
FROM Objects
WHERE 
    ObjectKey LIKE '%[^0-9a-zA-Z !"#$%&''()*+,\-./:;<=>?@\[\^_`{|}~\]\\]%' ESCAPE '\'
3
petejamd

Ce n'est probablement pas la meilleure solution, mais peut-être une requête comme:

SELECT *
FROM yourTable
WHERE yourTable.yourColumn LIKE '%[^0-9a-zA-Z]%'

Remplacez l'expression "0-9a-zA-Z" par quelque chose qui capture l'ensemble complet ASCII (ou un sous-ensemble que contiennent vos données).

2
Chris Shaffer

Si vous recherchez un caractère Unicode spécifique, vous pouvez utiliser quelque chose comme ci-dessous.

   select  Fieldname from 
     (
      select Fieldname,
             REPLACE(Fieldname COLLATE Latin1_General_BIN,
             NCHAR(65533) COLLATE Latin1_General_BIN,
             'CustomText123') replacedcol
      from table
     ) results where results.replacedcol like '%CustomText123%'
1
Mohan Billakanti

J'ai commencé avec la solution de @ CC1960 mais j'ai trouvé un cas d'utilisation intéressant qui l'a fait échouer. Il semble que SQL Server assimile certains caractères Unicode à leurs approximations non Unicode. Par exemple, SQL Server considère le caractère Unicode "virgule pleine largeur" ​​( http://www.fileformat.info/info/unicode/char/ff0c/index.htm ) identique à un standard ASCII virgule lorsque comparé dans une clause WHERE.

Pour contourner ce problème, demandez à SQL Server de comparer les chaînes comme binaires. Mais rappelez-vous que les binaires nvarchar et varchar ne correspondent pas (16 bits vs 8 bits), vous devez donc reconvertir votre varchar en nvarchar avant de faire la comparaison binaire:

select *
from my_table
where CONVERT(binary(5000),my_table.my_column) != CONVERT(binary(5000),CONVERT(nvarchar(1000),CONVERT(varchar(1000),my_table.my_column)))
1
neuracnu

Ma réponse précédente était de confondre les données UNICODE/non-UNICODE. Voici une solution qui devrait fonctionner pour toutes les situations, même si je rencontre toujours des anomalies. Il semble que certains caractères unicode non ASCII pour les caractères en exposant soient confondus avec le caractère numérique réel. Vous pourrez peut-être jouer avec les classements pour contourner cela.

J'espère que vous avez déjà une table de nombres dans votre base de données (ils peuvent être très utiles), mais juste au cas où j'aurais inclus le code pour le remplir partiellement également.

Vous devrez peut-être également jouer avec la plage numérique, car les caractères unicode peuvent aller au-delà de 255.

CREATE TABLE dbo.Numbers
(
    number  INT NOT NULL,
    CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (number)
)
GO
DECLARE @i INT

SET @i = 0

WHILE @i < 1000
BEGIN
    INSERT INTO dbo.Numbers (number) VALUES (@i)

    SET @i = @i + 1
END
GO

SELECT *,
    T.ID, N.number, N'%' + NCHAR(N.number) + N'%'
FROM
    dbo.Numbers N
INNER JOIN dbo.My_Table T ON
    T.description LIKE N'%' + NCHAR(N.number) + N'%' OR
    T.summary LIKE N'%' + NCHAR(N.number) + N'%'
and t.id = 1
WHERE
    N.number BETWEEN 127 AND 255
ORDER BY
    T.id, N.number
GO
0
Tom H