在 table 中检测非 ASCII 的 UNICODE 字符

Detect UNICODE characters that are not ASCII in table

我有以下 table:

Select
   name,
   address,
   description
from dbo.users

我想在所有这些 table 中搜索任何 UNICODE 而非 ASCII 字符。这可能吗?

据我所知,似乎没有内置函数。一种蛮力方法是将每个字符传递给 ascii,然后将结果传递给 char 并检查它是否 returns '?',这意味着该字符超出范围。您可以使用以下代码编写一个 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

您可以非常简单地找到非 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))

如果要判断NVARCHAR/NCHAR/NTEXT列中是否有不能转换为VARCHAR的字符,需要转换VARCHAR 使用用于该特定列的排序规则的 _BIN2 变体。例如,如果特定列使用 Albanian_100_CI_AS,则您将为测试指定 Albanian_100_BIN2。使用 _BIN2 排序规则的原因是非二进制排序规则只会找到至少有一个字符在代码页中根本没有任何映射并因此被转换为 ? 的实例.但是,非二进制排序规则不会捕获某些字符没有直接映射到代码页但具有 "best fit" 映射的实例。例如,上标 2 字符 ² 在代码页 1252 中有直接映射,因此绝对没有问题。另一方面,它在代码页 1250 中没有直接映射(由阿尔巴尼亚排序规则使用),但它确实有一个 "best fit" 映射,可将其转换为常规 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));