【问题标题】:SQL Server 2014 Hashbytes of a nvarchar(max) result is nvarchar(max)SQL Server 2014 Hashbytes 的 nvarchar(max) 结果是 nvarchar(max)
【发布时间】:2016-07-30 04:10:48
【问题描述】:

使用 SQL Server 2014 我有一个表,其中有一个名为 [ASCII File]nvarchar(max) 列,其中可以包含许多 K 的 ASCII 文本文件。然后我想对该文件执行 MD5 哈希字节,生成的哈希应该始终为 20 个字节。

好吧,当我选择 hashbytes('MD5', [ASCII File]) 时,查询完成并出现错误

消息 8152,第 16 级,状态 10,第 4 行
字符串或二进制数据将被截断。

我尝试时收到相同的消息

left(hashbytes('MD5', [ASCII File]), 50)

我尝试时收到相同的消息

convert(varchar(50), hashbytes('MD5', [ASCII File]))

似乎因为我正在做 hashbytes 的列是 nvarchar(max),所以 hashbytes 函数的结果也是 nvarchar(max)

您能告诉我如何使结果达到预期的 20 长,而不是必须截断的长吗?

【问题讨论】:

    标签: sql-server hashbytes


    【解决方案1】:

    似乎因为我正在做 hashbytes 的字段是 nvarchar(max),所以 hashbytes 的结果是 nvarchar(max)。

    不,这是不可能的,尤其是因为HASHBYTES 的返回值是VARBINARY。此外,由于您的测试只是 SELECT 语句而不是 INSERT 语句,因此返回值无法获得截断错误。截断错误来自 input 值。正如 HASHBYTES 的链接 MSDN 页面中所述(对于 SQL Server 2012 和 2014):

    允许的输入值限制为 8000 字节。输出符合算法标准:MD2、MD4、MD5为128位(16字节); SHA 和 SHA1 为 160 位(20 字节); SHA2_256 为 256 位(32 字节),SHA2_512 为 512 位(64 字节)。

    这确实说明了一切:输入限制为 8000 字节,输出是基于指定算法的固定字节数。

    针对 SQL Server 2016(已删除 8000 字节限制)的更新文档指出:

    对于 SQL Server 2014 及更早版本,允许的输入值限制为 8000 个字节。

    你可以运行一个简单的测试:

    DECLARE @Test NVARCHAR(MAX) = REPLICATE(CONVERT(NVARCHAR(MAX), N't'), 50000);
    SELECT LEN(@Test);
    SELECT HASHBYTES('MD5', @Test);
    

    返回:

    50000
    
    Msg 8152, Level 16, State 10, Line 3
    String or binary data would be truncated.
    

    如果要在 2016 之前的 SQL Server 版本中将超过 8000 个字节传递给哈希函数,则需要使用 SQLCLR。您可以编写自己的函数,也可以下载并安装SQL# SQLCLR 库(我创建的)的免费版本,并使用 Util_HashUtil_HashBinary功能:

    DECLARE @Test NVARCHAR(MAX) = REPLICATE(CONVERT(NVARCHAR(MAX), N't'), 50000);
    SELECT LEN(@Test);
    SELECT SQL#.Util_Hash('MD5', CONVERT(VARBINARY(MAX), @Test));
    SELECT SQL#.Util_HashBinary('MD5', CONVERT(VARBINARY(MAX), @Test));
    

    返回:

    50000
    40752EB301B41EEAEB309348CE9711D6
    0x40752EB301B41EEAEB309348CE9711D6
    

    更新

    如果使用 VARCHAR(MAX) 列或变量但字符数不超过 8000(或 NVARCHAR(MAX) 列或变量不超过 4000 个字符),则不会出现问题,一切都会按预期工作:

    DECLARE @Test VARCHAR(MAX) = REPLICATE('t', 5000);
    SELECT LEN(@Test) AS [Characters], 
           HASHBYTES('MD5', @Test) AS [MD5];
    

    返回:

    5000    0x6ABFBA10B49157F2EF8C85862B6E6313
    

    【讨论】:

    • 你误会了。 8000 个字符不是问题。我的测试数据文件仅低于限制 1k 或 2k 的方式,但由于 Column 被定义为 varchar(max) 它会引发该错误。该列被定义为 varchar(max) 因为将来我们可能有也可能没有更大的文件,我们将使用 SQL Server 2016。
    • @John 是的,我确实误会了,但那是因为你在问题“ASCII text file of many K”中陈述了错误鉴于您刚刚在上面的评论中澄清的细节,您所描述的不可能。我更新了我的答案,在最后添加了一个示例,表明这是可行的。正如我和文档所说:HASHBYTES 的输出始终是VARBINARY(8000)。也不可能从简单的 SELECT 语句的返回值中获取截断错误。返回值只有在插入小于提供值的字段时才会出现截断错误。
    【解决方案2】:

    在 SQL Server 2016 中,我们不再有 HASHBYTES 函数的输入参数长度问题。

    DECLARE @Test NVARCHAR(MAX); 
    SET @Test = REPLICATE(CONVERT(NVARCHAR(MAX), N't'), 50000000);
    SELECT LEN(@Test);
    SELECT HASHBYTES('SHA2_512', @Test);
    

    HASHBYTES (Transact-SQL)

    【讨论】:

      【解决方案3】:

      如果您正在尝试转换 sql 中已有的大型 varbinary 或图像文件,那么有一些内置函数可以做到这一点(可能从 2014 年开始),这个简单的函数将适用于 varbinary(max) 和较旧的 Image字段..

      /****** Object:  UserDefinedFunction [dbo].[MD5Bin]    Script Date: 16/07/2018 11:04:26 ******/
      
      SET ANSI_NULLS ON
      GO
      
      SET QUOTED_IDENTIFIER ON
      GO
      
      -- ==================================================
      -- Author:    Darren Steven
      -- Create date: 16/07/2018
      -- Description: Hashes a binary or image field with MD5
      -- ==================================================
      CREATE FUNCTION [dbo].[MD5Bin](@value varbinary(max))
      RETURNS varchar(32)
      AS
      BEGIN
        RETURN SUBSTRING(master.sys.fn_sqlvarbasetostr(master.sys.fn_repl_hash_binary(@value)),3,32);
      END
      GO
      

      然后只需使用您的选择调用该函数:

      SELECT dbo.MD5Bin(imageFieldName) from dbo.yourTable 
      

      【讨论】:

      • 适用于 SQL2008R2
      【解决方案4】:

      在 sql 2016 中删除了 HASHBYTES (Transact-SQL) 函数 8,000 字节的输入长度限制

      基于以下算法的是输出数据大小 MD2、MD4 和 MD5 为 128 位(16 字节); SHA 和 SHA1 为 160 位(20 字节); SHA2_256 为 256 位(32 字节) SHA2_512 为 512 位(64 字节)。

      【讨论】:

        猜你喜欢
        • 2010-09-08
        • 2018-05-22
        • 1970-01-01
        • 2015-11-12
        • 1970-01-01
        • 2013-12-12
        • 1970-01-01
        • 2017-10-12
        • 2011-01-09
        相关资源
        最近更新 更多