Обнаружение символов UNICODE, которые не являются ASCII в таблице

У меня есть следующая таблица:

Select
   name,
   address,
   description
from dbo.users

Я хотел бы найти во всей этой таблице любые символы UNICODE, но не ASCII. Это возможно?


person whytheq    schedule 23.04.2015    source источник
comment
Вы хотите вернуть сами символы или только идентифицировать записи с такими символами? @johnnybell уже ответил на последний случай.   -  person shree.pat18    schedule 23.04.2015
comment
@shree.pat18 shree.pat18 подойдут только записи ... какой-то указатель, чтобы я мог найти виновных   -  person whytheq    schedule 23.04.2015


Ответы (3)


Если вы хотите определить, есть ли в столбце NVARCHAR / NCHAR / NTEXT какие-либо символы, которые нельзя преобразовать в VARCHAR, вам необходимо преобразовать в VARCHAR, используя _BIN2 вариант сопоставления, используемый для этого конкретного столбца. Например, если в определенном столбце используется Albanian_100_CI_AS, для теста следует указать Albanian_100_BIN2. Причина использования сопоставления _BIN2 заключается в том, что недвоичные сопоставления будут находить только те экземпляры, где есть хотя бы один символ, который вообще не имеет никакого сопоставления в кодовой странице и, таким образом, преобразуется в ?. Но недвоичные сопоставления не улавливают экземпляры, в которых есть символы, которые не имеют прямого сопоставления с кодовой страницей, а вместо этого имеют «наилучшее соответствие». Например, символ верхнего индекса 2, ², имеет прямое сопоставление с кодовой страницей 1252, так что здесь определенно нет проблем. С другой стороны, у него нет прямого сопоставления в кодовой странице 1250 (используемой албанскими сопоставлениями), но у него есть «наилучшее соответствие», которое преобразует его в обычный 2. Проблема с небинарной сортировкой заключается в том, что 2 будет равняться ², и поэтому она не будет зарегистрирована как строка, которая не может быть преобразована в VARCHAR. Например:

SELECT CONVERT(VARCHAR(MAX), N'²' COLLATE French_100_CI_AS); -- Code Page 1252
-- ²
SELECT CONVERT(VARCHAR(MAX), N'²' COLLATE Albanian_100_CI_AS); -- Code Page 1250
-- 2

SELECT CONVERT(VARCHAR(MAX), N'²' COLLATE Albanian_100_CI_AS)
WHERE  N'²' <> CONVERT(NVARCHAR(MAX),
                       CONVERT(VARCHAR(MAX), N'²' COLLATE Albanian_100_CI_AS));
-- (no rows returned)

SELECT CONVERT(VARCHAR(MAX), N'²' COLLATE Albanian_100_BIN2)
WHERE  N'²' <> CONVERT(NVARCHAR(MAX),
                       CONVERT(VARCHAR(MAX), N'²' COLLATE Albanian_100_BIN2));
-- 2

В идеале вы должны явно преобразовать обратно в NVARCHAR, чтобы код ясно понимал, что он делает, хотя, если этого не сделать, все равно будет неявно преобразовано обратно в NVARCHAR, поэтому поведение будет одинаковым в любом случае.

Обратите внимание, что используются только MAX типов. Не используйте NVARCHAR(4000) или VARCHAR(4000), иначе вы можете получить ложные срабатывания из-за усечения данных в столбцах NVARCHAR(MAX).

Таким образом, с точки зрения примера кода в вопросе запрос будет (при условии, что используется сопоставление Latin1_General):

SELECT usr.*
FROM   dbo.[users] usr
WHERE  usr.[name] <> CONVERT(NVARCHAR(MAX),
               CONVERT(VARCHAR(MAX), usr.[name] COLLATE Latin1_General_100_BIN2))
OR     usr.[address] <> CONVERT(NVARCHAR(MAX),
               CONVERT(VARCHAR(MAX), usr.[address] COLLATE Latin1_General_100_BIN2))
OR     usr.[description] <> CONVERT(NVARCHAR(MAX),
               CONVERT(VARCHAR(MAX), usr.[description] COLLATE Latin1_General_100_BIN2));
person Solomon Rutzky    schedule 30.04.2019

Вы можете найти символы, отличные от ASCII, довольно просто:

SELECT NAME, ADDRESS, DESCRIPTION
FROM DBO.USERS
WHERE NAME != CAST(NAME AS VARCHAR(4000))
OR ADDRESS != CAST(ADDRESS AS VARCHAR(4000))
OR DESCRIPTION != CAST(DESCRIPTION AS VARCHAR(4000))
person John Bell    schedule 23.04.2015
comment
ну бежит. Просто нужно создать текст с чистым юникодом, чтобы успокоить меня, что он подчеркивает проблемы. - person whytheq; 23.04.2015
comment
Спасибо Джонни: SELECT ('vsdǣf' = CAST('vsdǣf' AS VARCHAR(4000))) ...работает - person whytheq; 23.04.2015
comment
Это не будет проверять ASCII; Он проверит кодировку базы данных по умолчанию, которая, вероятно, не является ASCII. Чтобы узнать, что это такое, см. этот ответ. - person Tom Blodget; 23.04.2015

Насколько я могу судить, встроенной функции для этого нет. Подход грубой силы состоит в том, чтобы передать каждый символ в ascii, а затем передать результат в char и проверить, возвращает ли он '?', что означало бы, что символ находится вне допустимого диапазона. Вы можете написать UDF с приведенным ниже кодом в качестве ссылки, но я думаю, что это очень неэффективное решение:

    declare @i int = 1
    declare @x nvarchar(10) = N'vsdǣf'
    declare @result nvarchar(100) = N''

    while (@i < len(@x))
    begin
     if char(ascii(substring(@x,@i,1))) = '?'
       begin
         set @result = @result + substring(@x,@i,1)       
       end
     set @i = @i+1
    end       

    select @result
person shree.pat18    schedule 23.04.2015