【问题标题】:Detect UNICODE characters that are not ASCII in table检测表中非 ASCII 的 UNICODE 字符
【发布时间】:2015-07-01 09:18:48
【问题描述】:

我有下表:

Select
   name,
   address,
   description
from dbo.users

我想在所有这个表中搜索任何 UNICODE 但不是 ASCII 的字符。这可能吗?

【问题讨论】:

  • 您是要返回字符本身还是仅识别具有此类字符的记录? @johnnybell 已经回答了后一种情况。
  • @shree.pat18 只是记录会做......某种指针,所以我可以找到“有罪的当事人”

标签: sql-server unicode ascii sql-server-2014 nvarchar


【解决方案1】:

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

【讨论】:

  • 运行良好。只需要在其中创建一些带有纯 unicode 的文本即可让我放心,它会突出问题
  • 谢谢约翰尼:SELECT ('vsdǣf' = CAST('vsdǣf' AS VARCHAR(4000))) ...works
  • 这不会检查 ASCII;它将检查数据库默认编码,这可能不是 ASCII。要了解它是什么,请参阅此answer
【解决方案2】:

如果要确定NVARCHAR / NCHAR / NTEXT 列中是否有任何字符无法转换为VARCHAR,则需要使用_BIN2 变体转换为VARCHAR用于该特定列的排序规则。例如,如果特定列使用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));

【讨论】:

    【解决方案3】:

    据我所知,似乎没有内置函数。蛮力方法是将每个字符传递给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
    

    【讨论】:

      猜你喜欢
      • 2013-05-27
      • 2016-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-14
      • 1970-01-01
      • 1970-01-01
      • 2014-01-25
      相关资源
      最近更新 更多