SQL 服务器和 PostgreSQL 之间的 MD5 值不匹配
MD5 value mismatch between SQL server and PostgreSQL
为了编写一些代码来对存储在 SQL 服务器和 PostgreSQL 中的数据进行一致性检查,我计划计算 table 数据的 MD5数据库,并验证它们是否相等。
只要数据是纯文本 (ANSI) 就可以正常工作,如下所示:
sql-server> SELECT master.dbo.fn_varbintohexstr(HashBytes('MD5', 'a'));
0x0cc175b9c0f1b6a831c399e269772661
postgres=# select MD5('a');
0cc175b9c0f1b6a831c399e269772661
现在,如果我尝试使用一些 Hangul(Korean) 字符,MD5 匹配失败:
sql-server> SELECT master.dbo.fn_varbintohexstr(HashBytes('MD5', '무'));
0x7827b52f65d9f7777d37071cbbbf7f2d
postgres=# select MD5('무');
cb3e9be1a3a28b355eabae1fa1e291b3
据我了解,不匹配的原因是 unicode 字符在 SQL 服务器中存储为 UCS-2 编码(固定 16 位编码),在 PostgreSQL 中存储为 UTF-8 编码。由于 MD5 适用于字符位,因此 SQL 服务器和 PostgreSQL.
中的字符位序列会有所不同
因为我主要处理韩文字符集,所以我在 PostgreSQL 中使用的解决方法是在计算哈希值之前将编码从 UTF-8 转换为 UHC(通用韩文字符集)以下:
postgres=# select MD5(CONVERT('무'::bytea,'UTF8','UHC'));
7827b52f65d9f7777d37071cbbbf7f2d
如您所见,上面的哈希值与SQL服务器的相同。
只要我处理的是韩文字符,一切都很好。但是有些 table 包含韩文和汉字的混合,在这种情况下转换失败:
postgres=# select MD5(CONVERT('무么'::bytea,'UTF8','UHC'));
ERROR: character 0xe4b988 of encoding "UTF8" has no equivalent in "UHC"
postgres=#
这个错误是有道理的,因为 UHC 字符集中没有对应的汉字。
我怎样才能让它发挥作用?基本上,我需要找到在 SQL 服务器中将 UCS-2 转换为 UTF-8 的方法,或者在计算 MD5 之前在 PostgreSQL 中将 UTF-8 转换为 UCS-2。我想在数据库引擎中执行所有这些操作,而不是在外部应用程序中加载数据来计算 MD5,因为一些 tables 有巨大的数据集。
SQL 服务器版本 2005
PostgreSQL 版本 9.1
根据这篇博文post.
提供的代码,应该可以在SQLServer 2005中实现一个NVARCHAR_TO_UTF8功能
SQL FUNCTION TO GET NVARCHAR FROM UTF-8 STORED IN VARCHAR
您必须执行反向转换。
有了 NVARCHAR_TO_UTF8 函数,应该可以在 SQL Server 2005 中计算出与在 PostgreSQL 9.1.
中相同的 MD5 哈希值
请注意,从 SQL Server 2014 开始,本机 UTF-8 支持仍然不存在:UTF-8 Feature Suggestion at Microsoft Connect
不幸的是,PostgreSQL 也不支持 UTF-16 / UCS-2。
但是,您可以编写一个函数,将 utf8 text
转换为 ucs2 二进制数据 (bytea
):
create or replace function text_to_ucs2be(input_in_utf8 text)
returns bytea
immutable
strict
language sql
as $$
select decode(string_agg(case
when code_point < 65536
then lpad(to_hex(code_point), 4, '0')
end, ''), 'hex')
from regexp_split_to_table(input_in_utf8, '') chr,
ascii(chr) code_point
$$;
create or replace function text_to_ucs2le(input_in_utf8 text)
returns bytea
immutable
strict
language sql
as $$
select decode(string_agg(case
when code_point < 65536
then lpad(to_hex(code_point & 255), 2, '0')
|| lpad(to_hex(code_point >> 8), 2, '0')
end, ''), 'hex')
from regexp_split_to_table(input_in_utf8, '') chr,
ascii(chr) code_point
$$;
注意:上面的这些函数将去除任何非 BMP 代码点(因此名称为 ucs2)。
以下语句应该会得到相同的结果:
-- on PostgreSQL
select md5(text_to_ucs2le('무'));
-- on SQL server
select master.dbo.fn_varbintohexstr(HashBytes('MD5', N'무'));
为了编写一些代码来对存储在 SQL 服务器和 PostgreSQL 中的数据进行一致性检查,我计划计算 table 数据的 MD5数据库,并验证它们是否相等。 只要数据是纯文本 (ANSI) 就可以正常工作,如下所示:
sql-server> SELECT master.dbo.fn_varbintohexstr(HashBytes('MD5', 'a'));
0x0cc175b9c0f1b6a831c399e269772661
postgres=# select MD5('a');
0cc175b9c0f1b6a831c399e269772661
现在,如果我尝试使用一些 Hangul(Korean) 字符,MD5 匹配失败:
sql-server> SELECT master.dbo.fn_varbintohexstr(HashBytes('MD5', '무'));
0x7827b52f65d9f7777d37071cbbbf7f2d
postgres=# select MD5('무');
cb3e9be1a3a28b355eabae1fa1e291b3
据我了解,不匹配的原因是 unicode 字符在 SQL 服务器中存储为 UCS-2 编码(固定 16 位编码),在 PostgreSQL 中存储为 UTF-8 编码。由于 MD5 适用于字符位,因此 SQL 服务器和 PostgreSQL.
中的字符位序列会有所不同因为我主要处理韩文字符集,所以我在 PostgreSQL 中使用的解决方法是在计算哈希值之前将编码从 UTF-8 转换为 UHC(通用韩文字符集)以下:
postgres=# select MD5(CONVERT('무'::bytea,'UTF8','UHC'));
7827b52f65d9f7777d37071cbbbf7f2d
如您所见,上面的哈希值与SQL服务器的相同。
只要我处理的是韩文字符,一切都很好。但是有些 table 包含韩文和汉字的混合,在这种情况下转换失败:
postgres=# select MD5(CONVERT('무么'::bytea,'UTF8','UHC'));
ERROR: character 0xe4b988 of encoding "UTF8" has no equivalent in "UHC"
postgres=#
这个错误是有道理的,因为 UHC 字符集中没有对应的汉字。
我怎样才能让它发挥作用?基本上,我需要找到在 SQL 服务器中将 UCS-2 转换为 UTF-8 的方法,或者在计算 MD5 之前在 PostgreSQL 中将 UTF-8 转换为 UCS-2。我想在数据库引擎中执行所有这些操作,而不是在外部应用程序中加载数据来计算 MD5,因为一些 tables 有巨大的数据集。
SQL 服务器版本 2005 PostgreSQL 版本 9.1
根据这篇博文post.
提供的代码,应该可以在SQLServer 2005中实现一个NVARCHAR_TO_UTF8功能SQL FUNCTION TO GET NVARCHAR FROM UTF-8 STORED IN VARCHAR
您必须执行反向转换。
有了 NVARCHAR_TO_UTF8 函数,应该可以在 SQL Server 2005 中计算出与在 PostgreSQL 9.1.
中相同的 MD5 哈希值请注意,从 SQL Server 2014 开始,本机 UTF-8 支持仍然不存在:UTF-8 Feature Suggestion at Microsoft Connect
不幸的是,PostgreSQL 也不支持 UTF-16 / UCS-2。
但是,您可以编写一个函数,将 utf8 text
转换为 ucs2 二进制数据 (bytea
):
create or replace function text_to_ucs2be(input_in_utf8 text)
returns bytea
immutable
strict
language sql
as $$
select decode(string_agg(case
when code_point < 65536
then lpad(to_hex(code_point), 4, '0')
end, ''), 'hex')
from regexp_split_to_table(input_in_utf8, '') chr,
ascii(chr) code_point
$$;
create or replace function text_to_ucs2le(input_in_utf8 text)
returns bytea
immutable
strict
language sql
as $$
select decode(string_agg(case
when code_point < 65536
then lpad(to_hex(code_point & 255), 2, '0')
|| lpad(to_hex(code_point >> 8), 2, '0')
end, ''), 'hex')
from regexp_split_to_table(input_in_utf8, '') chr,
ascii(chr) code_point
$$;
注意:上面的这些函数将去除任何非 BMP 代码点(因此名称为 ucs2)。
以下语句应该会得到相同的结果:
-- on PostgreSQL
select md5(text_to_ucs2le('무'));
-- on SQL server
select master.dbo.fn_varbintohexstr(HashBytes('MD5', N'무'));