我目前正在将数据从 Oracle 迁移到 SQL Server,并且在尝试验证迁移后的数据时遇到了问题。
环境细节:
- Oracle 12 - AL32UTF8 字符集
- 客户端 - NLS_LANG - WE8MSWIN1252
- VARCHAR2 字段
SQL 服务器 2016
- Latin1_General_CI_AS 排序规则
- NVARCHAR 字段
我在 Oracle 上使用 DBMS_CRYPTO.HASH 生成整行的校验和,然后复制到 SQL 并使用 HASHBYTES 生成整行的校验和,然后进行比较以验证数据匹配。
校验和与所有行匹配,但具有多字节字符的行除外。
例如,具有以下字符的行: ◦ 在校验和中不匹配,即使数据传输正确。当我在 Oracle 中使用 DUMP 或在 SQL Server 中转换为 VARBINARY 时,数据完全匹配,但该字符的字节除外。
在 SQL Server 中,字节为 0xE625,在 Oracle 中为 0x25E6。
为什么它们的顺序不同,是否有可靠的方法将一个转换为另一个以确保另一端的校验和与多字节字符的字符串匹配?
NVARCHAR
//列的排序规则与NCHAR
用于NTEXT
在该列中存储数据的编码无关。NVARCHAR
数据始终为UTF-16 Little Endian (LE)。数据的整理NVARCHAR
只影响排序和比较。排序规则确实会影响VARCHAR
数据的编码,因为排序规则确定了用于将数据存储在该列/变量/文字中的代码页,但我们在这里不处理。正如sepupic 所提到的,当您以二进制形式查看数据时,您看到的是字节序的差异(Oracle 使用 Big Endian,而 SQL Server 使用 Little Endian)。然而,当您在 Oracle 中查看二进制形式的字符串时,您看到的并不是数据的实际存储方式。您使用
AL32UTF8
的是 UTF-8,它将该字符编码为 3 个字节,而不是 2 个字节,如:E2, 97, A6
.此外,只有“a”的行的哈希值不可能相同,但当它们包含“◦”时则不然,除非 Oracle 中的哈希值是在没有转换的情况下完成的,因此使用 UTF-8 编码,并且SQL Server 中的散列不小心转换为
VARCHAR
第一个。否则,没有哈希算法会像您描述的那样运行,您可以通过在 SQL Server 中运行以下命令来验证:在 Oracle 中,您应该使用该
CONVERT
函数将字符串放入AL16UTF16LE
编码中,然后对该值进行哈希处理。这应该与 SQL Server 所拥有的相匹配。例如,您可以看到White Bullet (U+25E6)的不同编码形式以及如何在dbfiddle及以下内容中使用CONVERT
withAL16UTF16LE
来纠正此问题:返回:
正如您在第 3 列中看到的那样,当字符集基于两个字节的顺序明显是 Little Endian 时,它被误报为 Big Endian。您还可以看到,在 UTF-16 中这两个字符都是两个字节,并且它们的顺序在Big Endian 和 Little Endian 之间是不同的,而不仅仅是 UTF-8 中大于 1 字节的字符。
鉴于所有这些,由于数据被存储为 UTF-8,但您通过该
DUMP
函数将其视为 UTF-16 Big Endian,您似乎已经将其转换为 UTF-16,但可能没有意识到默认值Oracle 中的 UTF-16 是大端。查看Oracle 文档词汇表页面上的“UTF-16”定义,它指出(我将以下句子分成两部分,以便更容易区分 BE 和 LE):
和:
PS 由于您
AL32UTF8
在 Oracle 中使用,因此您应该Latin1_General_100_CI_AS_SC
在 SQL Server 中使用排序规则,而不是Latin1_General_CI_AS
. 您使用的那个较旧并且不完全支持补充字符(如果存在,则不会丢失数据,但内置函数将它们作为 2 个字符而不是单个实体处理)。您所看到的是用于存储字符的
Little-Endian
编码(更准确地说,它使用)。SQL Server
Unicode
UCS-2 LE
更多
Little-Endian
信息:Big Endian 和 Little Endian 字节顺序之间的区别我不知道怎么可能
Unicode
存储在SQL Server
、 转换为的所有binary
字符都是“反转的”,我的意思是,要查看真正的代码,您应该将它们分成 2 组bytes
并反转每对中的顺序。例子:
结果是
如您所见,在
Unicode
字符字节被反转的情况下:“a”表示为0x6100
而不是表示为0x0061
.同样的故事是关于
0x25E6
那是真实的Unicode
代码,而binary
在SQL Server
你看来它是真实的代码0xE625
,即inverted
。