在SQL Server 2008和以前的版本中读取带有奇怪字符的字符串的问题

时间:2017-08-15 12:40:33

标签: sql-server sql-server-2008 tsql unicode

今天是我的问题:我们将一些信息从文本文件加载到SQL Server表中。问题是,对于一个字段,我意识到字符串值有点奇怪。

当我使用SQL Server客户端2008或之前的查询表时,我得到了这个结果集:

enter image description here

即使我尝试运行如下所示的查询,结果集也是空的:

select REPLACE(LTRIM(RTRIM(cust_po)), '  ', ' ') 
from dbo.test_char
where cust_po like '%076929%'

奇怪的是:使用SQL Server 2016,只是打开表看起来很正常,尽管查询也没有返回任何结果:

enter image description here

现在,根据@SolomonRutzky的一些指示,我已经执行了下面的查询以获得varbinary中的结果

enter image description here

新结果为:0x300037003600390032003900BC05BC05BC05BC05BC05BC05BC05BC05BC05

怎么可能把它当作一个没有奇怪的字符或空格的varchar,或者在哪里呢?

2 个答案:

答案 0 :(得分:2)

发生了什么事?

现在已发布数据的VARBINARY表示,问题更加清晰。

正如我们所看到的,带有“奇数”字符的两行是:

0x300037003600390032003900BC05BC05BC05BC05BC05BC05BC05BC05BC05

此列为NVARCHAR,表示编码为UTF-16 Little Endian。作为UTF-16意味着我们查看每个2字节的块(字符将是这些2字节块的1或2组),而Little Endian意味着每个2字节块中的字节的顺序相反。意思是,第一个字符是3000(2个字节),这相当于代码点U + 0030,即0。下一个字符是3700,即代码点U + 0037,即7。依此类推3900,这是值中的最终9

然后我们得到9组BC05,即Code Point U + 05BC,Hebrew Point Dagesh or Mapiq。这就是它变得有趣的地方,因为这里有三个不同的事情:

  1. 显示的是正在使用的字体的功能。并非所有字体都能正确处理所有字符,甚至不能为所有字符设置映射。这就是为什么你在结果网格中看到左边的一系列圆圈,在行编辑器的最左边看到一个点(很难看到,特别是如果你不知道要查找它,但仔细观察行编辑器图像和前两行在0左侧都有一个小点。
  2. 希伯来语是一种从右到左的语言,它解释了为什么结果网格中的圆圈位于左侧,即使数据中的字符位于数字之后。
  3. 这个特殊字符是一个组合字符,这意味着它应该附加到它前面的字符。由于不同的字体以不同的方式显示字符,因此您可以获得结果网格和行编辑器之间的变化。结果网格字体似乎没有正确处理此字符,而行编辑器。但是,在正确处理它时,我们看到这个组合字符不允许显示倍数,所以我们看到的是最左边的一个点而不是9个点(但是所有9个都在那里,只有一个在另一个上面
  4. 要查看此操作,请执行以下查询:

    SELECT N'4   11' + REPLICATE(NCHAR(0x05BC), 10) + N'  88  f ';
    

    返回:

    4 11ּּּּּּּּּּ88 f

    注意第二个1左侧的单个点。即使字符串中有10个(由于REPLICATE),也只有一个点。嗯,这就是我的浏览器显示的内容,如下图所示:

    enter image description here

    然而,我在结果网格中看到了以下内容:

    enter image description here

    注意点位于第一个,而不是第二个1的左侧,以及1188之间f的位置,< / p>

    而且,如果我从结果网格中复制并粘贴到查询编辑器中,我会看到:

    enter image description here

    请注意,有几个红点。

    为什么where cust_po like '%076929%'没有返回任何行?

    这是由于字符串比较做了它应该做的事情并应用语言规则。字节的顺序并不重要,重要的是如何从人类阅读角度查看渲染字符串。由于这个特殊字符是一个组合字符,它不会出现在前面的字符之后,它是它的一部分。这意味着,9值中的第二个076929不再是9,而是9 + Dagesh。

    SELECT 1 WHERE
    N'123' + REPLICATE(NCHAR(0x05BC), 5) LIKE N'%123' + REPLICATE(NCHAR(0x05BC), 4) + N'%';
    -- no rows returned
    
    SELECT 2 WHERE
    N'123' + REPLICATE(NCHAR(0x05BC), 5) LIKE N'%123' + REPLICATE(NCHAR(0x05BC), 5) + N'%';
    -- 2
    
    SELECT 3 WHERE N'9' + NCHAR(0x05BC) = N'9'
    -- no rows returned
    

    你如何摆脱这些角色?

    假设此U + 05BC字符是数据中唯一的问题,您可以进行简单的替换。您已尝试过此操作,但REPLACE找不到匹配项。发生这种情况时,您需要使用二进制排序规则,如下所示:

    SELECT REPLACE(N'4   11' + REPLICATE(NCHAR(0x05BC), 5) + N'  88  f ',
                   NCHAR(0x05BC) COLLATE Latin1_General_100_BIN2,
                   N'~');
    -- 4   11~~~~~  88  f 
    

答案 1 :(得分:1)

首先 - 当你让@solomon Rutzky查看你的代码时,你会得到很好的帮助;)

我目前没有此版本的NVarchar,但如果您只想要列中的数值,则可以使用Blob。解决方案看起来像这样。

-- sample data with a couple characters to screw you up. 
DECLARE @table TABLE (somestring nvarchar(100));
INSERT @table
SELECT CONCAT(NCHAR(365), '123') UNION ALL
SELECT CONCAT(CHAR(0), '789');

-- solution
SELECT *
FROM @table t
CROSS APPLY dbo.DigitsOnlyEE(t.somestring);

结果:

somestring   DigitsOnly
------------ ------------
ŭ123         123
 789         789