SQL Server和PostgreSQL之间的MD5值不匹配

时间:2015-03-01 03:09:41

标签: sql-server postgresql unicode utf-8 utf-16

为了编写一些代码来对SQL Server和PostgreSQL中存储的数据进行一致性检查,我计划计算两个数据库的表数据的MD5,并验证它们是否相等。 只要数据是纯文本(ANSI),就可以正常工作,如下所示:

sql-server> SELECT master.dbo.fn_varbintohexstr(HashBytes('MD5', 'a'));
0x0cc175b9c0f1b6a831c399e269772661


postgres=# select MD5('a');
0cc175b9c0f1b6a831c399e269772661

现在,如果我尝试使用一些韩文(韩文)字符,MD5匹配失败:

sql-server> SELECT master.dbo.fn_varbintohexstr(HashBytes('MD5', '무'));
0x7827b52f65d9f7777d37071cbbbf7f2d


postgres=# select MD5('무');
cb3e9be1a3a28b355eabae1fa1e291b3

根据我的理解,不匹配的原因是unicode字符存储为SQL Server中的UCS-2编码(固定16位编码)和PostgreSQL中的UTF-8编码。由于MD5适用于字符位,因此SQL Server和PostgreSQL中的字符位序列会有所不同。

由于我一直主要处理韩文字符集,我在PostgreSQL中使用的解决方法是在计算哈希值之前将编码从UTF-8转换为UHC(通用韩文字符集),如下所示:

postgres=# select MD5(CONVERT('무'::bytea,'UTF8','UHC'));
7827b52f65d9f7777d37071cbbbf7f2d

如您所见,上面的哈希值与SQL Server的哈希值相同。

只要我处理韩文字符,一切都很好。但是有些表包含韩文和汉字的混合,在这种情况下转换失败:

postgres=# select MD5(CONVERT('무么'::bytea,'UTF8','UHC'));
ERROR:  character 0xe4b988 of encoding "UTF8" has no equivalent in "UHC"
postgres=# 

错误是有道理的,因为UHC字符集中没有相应的中文字符。

我怎样才能让它发挥作用?基本上,我需要找到在SQL Server中将UCS-2转换为UTF-8的方法,或者在计算MD5之前将PostFSQL中的UTF-8转换为UCS-2。我想在数据库引擎中执行所有这些操作,而不是在外部应用程序中加载数据来计算MD5,因为有些表具有巨大的数据集。

SQL Server版本2005 PostgreSQL版本9.1

2 个答案:

答案 0 :(得分:3)

不幸的是,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'무'));

答案 1 :(得分:1)

应该可以根据此博客文章中提供的代码在SQL Server 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