【问题标题】:CHECKSUM and CHECKSUM_AGG: What's the algorithm?CHECKSUM 和 CHECKSUM_AGG:算法是什么?
【发布时间】:2013-05-01 09:52:26
【问题描述】:

我们对sql server中的一些数据进行校验如下:

declare @cs int;
select 
    @cs = CHECKSUM_AGG(CHECKSUM(someid, position))
from 
    SomeTable
where 
    userid = @userId
group by 
    userid;

这些数据随后会与客户共享。我们希望能够在客户端重复校验和......但是似乎没有关于如何计算上述函数中的校验和的任何信息。谁能赐教?

【问题讨论】:

  • 我怀疑使用的校验和算法太差了,以至于 MS 都不好意思告诉任何人它是什么!

标签: sql-server sql-server-2008 checksum


【解决方案1】:

在 SQL Server 论坛上,page 上声明:

SQL Server 中内置的 CHECKSUM 函数建立在一系列 4 位左旋异或运算之上。更多解释请参见post

【讨论】:

    【解决方案2】:

    CHECKSUM 函数不能提供质量非常好的校验和,而且 IMO 对于大多数用途来说毫无用处。据我所知,该算法尚未发布。如果您想要检查自己是否可以重现,请使用 HashBytes 函数和标准的已发布算法之一,例如 MD5 或 SHA。

    【讨论】:

      【解决方案3】:

      //SQL和C#镜像乌克兰的快速hash求和

           private Int64 HASH_ZKCRC64(byte[] Data)
          {
              Int64 Result = 0x5555555555555555;
              if (Data == null || Data.Length <= 0) return 0;
              int SizeGlobalBufer = 8000;
              int Ost = Data.Length % SizeGlobalBufer;
              int LeftLimit = (Data.Length / SizeGlobalBufer) * SizeGlobalBufer;
      
              for (int i = 0; i < LeftLimit; i += 64)
              {
                  Result = Result
                  ^ BitConverter.ToInt64(Data, i)
                  ^ BitConverter.ToInt64(Data, i + 8)
                  ^ BitConverter.ToInt64(Data, i + 16)
                  ^ BitConverter.ToInt64(Data, i + 24)
                  ^ BitConverter.ToInt64(Data, i + 32)
                  ^ BitConverter.ToInt64(Data, i + 40)
                  ^ BitConverter.ToInt64(Data, i + 48)
                  ^ BitConverter.ToInt64(Data, i + 56);
                   if ((Result & 0x0000000000000080) != 0)
                   Result = Result ^ BitConverter.ToInt64(Data, i + 28); 
              }
      
              if (Ost > 0)
              {
                 byte[] Bufer = new byte[SizeGlobalBufer];
                 Array.Copy(Data, LeftLimit, Bufer, 0, Ost);
                 for (int i = 0; i < SizeGlobalBufer; i += 64)
                 {
                     Result = Result
                     ^ BitConverter.ToInt64(Bufer, i)
                     ^ BitConverter.ToInt64(Bufer, i + 8)
                     ^ BitConverter.ToInt64(Bufer, i + 16)
                     ^ BitConverter.ToInt64(Bufer, i + 24)
                     ^ BitConverter.ToInt64(Bufer, i + 32)
                     ^ BitConverter.ToInt64(Bufer, i + 40)
                     ^ BitConverter.ToInt64(Bufer, i + 48)
                     ^ BitConverter.ToInt64(Bufer, i + 56);
                     if ((Result & 0x0000000000000080)!=0)
                     Result = Result ^ BitConverter.ToInt64(Bufer, i + 28); 
                 }
              }
      
              byte[] MiniBufer = BitConverter.GetBytes(Result);
              Array.Reverse(MiniBufer);
              return BitConverter.ToInt64(MiniBufer, 0);
      
              #region SQL_FUNCTION
              /*  CREATE FUNCTION [dbo].[HASH_ZKCRC64] (@data as varbinary(MAX)) Returns bigint
                  AS
                  BEGIN
                  Declare @I64 as bigint Set @I64=0x5555555555555555
                  Declare @Bufer as binary(8000)
                  Declare @i as int Set @i=1
                  Declare @j as int 
                  Declare @Len as int Set @Len=Len(@data)     
      
                  if ((@data is null) Or (@Len<=0)) Return 0
      
                    While @i<=@Len
                    Begin
                     Set @Bufer=Substring(@data,@i,8000)
                     Set @j=1
                         While @j<=8000
                         Begin
                          Set @I64=@I64 
                          ^ CAST(Substring(@Bufer,@j,   8) as bigint) 
                          ^ CAST(Substring(@Bufer,@j+8, 8) as bigint) 
                          ^ CAST(Substring(@Bufer,@j+16,8) as bigint)
                          ^ CAST(Substring(@Bufer,@j+24,8) as bigint)
                          ^ CAST(Substring(@Bufer,@j+32,8) as bigint)
                          ^ CAST(Substring(@Bufer,@j+40,8) as bigint)
                          ^ CAST(Substring(@Bufer,@j+48,8) as bigint)
                          ^ CAST(Substring(@Bufer,@j+56,8) as bigint)
                          if @I64<0 Set @I64=@I64 ^ CAST(Substring(@Bufer,@j+28,8) as bigint)      
                          Set @j=@j+64    
                         End;  
                     Set @i=@i+8000
                    End
                  Return @I64
                  END
               */
              #endregion
      
         }
      

      【讨论】:

        【解决方案4】:

        我发现了CHECKSUM 算法,至少对于 ASCII 字符。我在 JavaScript 中创建了一个证明(参见https://stackoverflow.com/a/59014293/9642)。

        简而言之:将每个字符向左旋转 4 位并异或一个代码。诀窍是找出“异或码”。以下是这些表格:

        var xorcodes = [
            0, 1, 2, 3, 4, 5, 6, 7,
            8, 9, 10, 11, 12, 13, 14, 15,
            16, 17, 18, 19, 20, 21, 22, 23,
            24, 25, 26, 27, 28, 29, 30, 31,
            0, 33, 34, 35, 36, 37, 38, 39,  //  !"#$%&'
            40, 41, 42, 43, 44, 45, 46, 47,  // ()*+,-./
            132, 133, 134, 135, 136, 137, 138, 139,  // 01234567
            140, 141, 48, 49, 50, 51, 52, 53, 54,  // 89:;<=>?@
            142, 143, 144, 145, 146, 147, 148, 149,  // ABCDEFGH
            150, 151, 152, 153, 154, 155, 156, 157,  // IJKLMNOP
            158, 159, 160, 161, 162, 163, 164, 165,  // QRSTUVWX
            166, 167, 55, 56, 57, 58, 59, 60,  // YZ[\]^_`
            142, 143, 144, 145, 146, 147, 148, 149,  // abcdefgh
            150, 151, 152, 153, 154, 155, 156, 157,  // ijklmnop
            158, 159, 160, 161, 162, 163, 164, 165,  // qrstuvwx
            166, 167, 61, 62, 63, 64, 65, 66,  // yz{|}~
        ];
        

        需要注意的主要是对字母数字的偏见(它们的代码相似且升序)。英文字母不分大小写使用相同的代码。

        我没有测试过高代码 (128+) 和 Unicode。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-01-05
          • 1970-01-01
          • 2015-01-08
          • 2012-08-13
          • 2018-07-21
          相关资源
          最近更新 更多