【问题标题】:Calculate Hash or Checksum for a table in SQL Server在 SQL Server 中计算表的哈希或校验和
【发布时间】:2010-12-06 07:52:48
【问题描述】:

我正在尝试为 SQL Server 2008 中的整个表计算校验和或哈希。我遇到的问题是该表包含 XML 列数据类型,它不能被校验和使用,并且必须首先转换为 nvarchar。所以我需要把它分解成两个问题:

  1. 计算一行的校验和,运行前架构未知。
  2. 计算所有行的校验和以获得全表校验和。

【问题讨论】:

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


    【解决方案1】:

    您可以使用CHECKSUM_AGG。它只需要一个参数,因此您可以使用CHECKSUM_AGG(CHECKSUM(*)) - 但这不适用于您的 XML 数据类型,因此您必须求助于动态 SQL。

    您可以从INFORMATION_SCHEMA.COLUMNS 动态生成列列表,然后将 int 插入到模板中:

    DECLARE @schema_name NVARCHAR(MAX) = 'mySchemaName';
    DECLARE @table_name NVARCHAR(MAX) = 'myTableName';
    DECLARE @column_list NVARCHAR(MAX);
    
    SELECT @column_list = COALESCE(@column_list + ', ', '')
            + /* Put your casting here from XML, text, etc columns */ QUOTENAME(COLUMN_NAME)
    FROM    INFORMATION_SCHEMA.COLUMNS
    WHERE   TABLE_NAME = @table_name
        AND TABLE_SCHEMA = @schema_name
    
    DECLARE @template AS varchar(MAX)
    SET @template = 'SELECT CHECKSUM_AGG(CHECKSUM({@column_list})) FROM {@schema_name}.{@table_name}'
    
    DECLARE @sql AS varchar(MAX)
    SET @sql = REPLACE(REPLACE(REPLACE(@template,
        '{@column_list}', @column_list),
        '{@schema_name}', @schema_name),
        '{@table_name}', @table_name)
    
    EXEC ( @sql )
    

    【讨论】:

    • 谢谢!我不得不稍微调整一下以适应数据类型的特殊情况,但我能够提出一些快速且基于此解决方案的东西。太棒了!
    • 请注意标识列,我也会使用 BINARY_CHECKSUM,因为这是区分大小写的。
    • REPLACE 需要 3 个参数。你的意思是:sql SET @sql = REPLACE( REPLACE( REPLACE( @template, '{@column_list}', @column_list ), '{@schema_name}', @schema_name ), '{@table_name}', @table_name);
    • 为什么这会在具有相同表的不同系统上产生不同的校验和?我什至使用带有链接服务器的 select into 将表以不同的名称复制到一个系统,并且在同一台机器上校验和是相同的,但与另一台机器不同,即使表是相同的。
    • @Paul-SebastianManole 是的,替换的第一行末尾应该有一个@template 至于不同的校验和,你有一个身份列或类似的东西吗?跨度>
    【解决方案2】:

    我修改了脚本以生成数据库中所有相关表的查询。

    USE myDatabase
    GO
    DECLARE @table_name sysname
    DECLARE @schema_name sysname
    SET @schema_name = 'dbo'
    
    DECLARE myCursor cursor
    FOR SELECT TABLE_NAME
          FROM INFORMATION_SCHEMA.TABLES T
         WHERE T.TABLE_SCHEMA = @schema_name
           AND T.TABLE_TYPE = 'BASE TABLE'
           AND T.TABLE_NAME NOT LIKE 'MSmerge%'
           AND T.TABLE_NAME NOT LIKE 'sysmerge%'
           AND T.TABLE_NAME NOT LIKE 'tmp%'
         ORDER BY T.TABLE_NAME
    
    OPEN myCursor
    
    FETCH NEXT 
    FROM myCursor
    INTO @table_name 
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
        DECLARE @column_list nvarchar(MAX)
        SET @column_list=''
    SELECT @column_list = @column_list + CASE WHEN DATA_TYPE IN ('xml','text','ntext','image sql_variant') THEN 'CONVERT(nvarchar(MAX),'
                                              ELSE ''
                                         END
                                       + QUOTENAME(COLUMN_NAME)
                                       + CASE WHEN DATA_TYPE IN ('xml','text','ntext','image sql_variant') THEN ' /* ' + DATA_TYPE + ' */)'
                                              ELSE ''
                                         END + ', '
      FROM INFORMATION_SCHEMA.COLUMNS
         WHERE TABLE_NAME = @Table_name
         ORDER BY ORDINAL_POSITION
    
        SET @column_list = LEFT(@column_list, LEN(@column_list)-1) -- remove trailing comma
    
        DECLARE @sql AS nvarchar(MAX)
        SET @sql = 'SELECT ''' + QUOTENAME(@schema_name) + '.' + QUOTENAME(@table_name) + ''' table_name,
           CHECKSUM_AGG(CHECKSUM(' + @column_list + ')) CHECKSUM
      FROM ' + QUOTENAME(@schema_name) + '.' + QUOTENAME(@Table_name) + ' WITH (NOLOCK)'
    
    
        PRINT  @sql
    
        FETCH NEXT 
        FROM myCursor
        INTO @table_name 
    
        IF @@FETCH_STATUS = 0
            PRINT  'UNION ALL'
    
    END
    
    CLOSE myCursor
    DEALLOCATE myCursor
    GO
    

    【讨论】:

      【解决方案3】:

      //SQL和C#镜像乌克兰的快速hash求和 //HASH_ZKCRC64 ///---------------------------------------------- -------------------------------------------------- ------------- 私有 Int64 HASH_ZKCRC64(byte[] 数据) { Int64 结果 = 0x5555555555555555; if (Data == null || Data.Length

              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
      
         }
      

      【讨论】:

      • 请格式化您的代码并用解释更新您的答案。
      猜你喜欢
      • 1970-01-01
      • 2022-09-23
      • 2023-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-22
      • 2013-01-10
      相关资源
      最近更新 更多