Unicode 到非 Unicode 的转换

Ram*_*ale 4 t-sql sql-server unicode collation character-encoding

我在NVarchar名为“邮政编码”的字段中有一些 Unicode 字符。当我将它们转换为 时,结果中Varchar有一个。?

我的代码是:

select PostalCode, cast((PostalCode) as varchar)) as val from  table
Run Code Online (Sandbox Code Playgroud)

结果是:

PostalCode       |   val
053000           | 053000?
Run Code Online (Sandbox Code Playgroud)

在这里我得到了?结果。有什么办法可以删除这些特殊字符吗?

Sol*_*zky 5

这里有几点需要注意:

  1. 如果您想准确查看其中有哪个字符,可以将值转换为该值,VARBINARY该值将为您提供字符串中所有字符的十六进制/二进制值,并且十六进制中没有“隐藏”字符的概念:

    DECLARE @PostalCode NVARCHAR(20);
    SET @PostalCode = N'053000'+ NCHAR(0x2008); -- 0x2008 = "Punctuation Space"
    SELECT @PostalCode AS [NVarCharValue],
           CONVERT(VARCHAR(20), @PostalCode) AS [VarCharValue],
           CONVERT(VARCHAR(20), RTRIM(@PostalCode)) AS [RTrimmedVarCharValue],
           CONVERT(VARBINARY(20), @PostalCode) AS [VarBinaryValue];
    
    Run Code Online (Sandbox Code Playgroud)

    返回:

    NVarCharValue   VarCharValue   RTrimmedVarCharValue   VarBinaryValue
    053000          053000?        053000?                0x3000350033003000300030000820
    
    Run Code Online (Sandbox Code Playgroud)

    NVARCHAR数据以 UTF-16 格式存储,以 2 字节集工作。查看最后 4 个十六进制数字来查看隐藏的 2 字节集是什么,我们看到“0820”。由于Windows和SQL Server是UTF-16 Little Endian(即UTF-16LE),因此字节顺序相反。翻转最后 2 个字节——08然后20——我们得到“2008”,这是我们通过添加的“标点符号空间” NCHAR(0x2008)

    另外,请注意,这RTRIM根本没有帮助。

  2. 简单地说,您可以将问号替换为任何内容:

    SELECT REPLACE(CONVERT(VARCHAR(20), [PostalCode]), '?', '');
    
    Run Code Online (Sandbox Code Playgroud)
  3. 更重要的是,您应该将该[PostalCode]字段转换为VARCHAR,以便它不存储这些字符。没有哪个国家/地区使用未在 ASCII 字符集中表示的字母,并且这些字母对 VARCHAR 数据类型无效,至少就我所读到的而言是如此(请参阅底部部分以获取参考资料)。事实上,允许的是 ASCII 的一个相当小的子集,这意味着您可以轻松地过滤(或者REPLACE在插入或更新时执行与上面所示相同的操作):

    ALTER TABLE [table] ALTER COLUMN [PostalCode] VARCHAR(20) [NOT]? NULL;
    
    Run Code Online (Sandbox Code Playgroud)

    请务必检查该列的当前NULL/NOT NULL设置,并使其与上面的 ALTER 语句中的设置相同,否则NULL如果未指定,则可能会更改为默认值。

  4. 如果您无法更改表的架构并且需要定期“清理”坏数据,您可以运行以下命令:

    ;WITH cte AS
    (
       SELECT *
       FROM   TableName
       WHERE  [PostalCode] <>
                      CONVERT(NVARCHAR(50), CONVERT(VARCHAR(50), [PostalCode]))
    )
    UPDATE cte
    SET    cte.[PostalCode] = REPLACE(CONVERT(VARCHAR(50), [PostalCode]), '?', '');
    
    Run Code Online (Sandbox Code Playgroud)

    请记住,如果表有数百万行,上述查询就无法高效工作。那时,需要通过循环以较小的集合进行处理。


作为参考,这里是关于邮政编码的维基百科文章,其中目前指出曾经使用的唯一字符是:

  • 阿拉伯数字“0”到“9”
  • ISO 基本拉丁字母表中的字母
  • 空格、连字符

关于字段的最大大小,这里是维基百科邮政编码列表