2009-10-13 41 views
25

Estoy tratando de calcular una suma de comprobación o un hash para una tabla completa en SQL Server 2008. El problema que me encuentro es que la tabla contiene un tipo de datos de columna XML, que no se puede usar por suma de comprobación y se debe convertir primero a nvarchar. Así que necesito dividirlo en dos problemas:Calcular Hash o Checksum para una tabla en SQL Server

  1. calcular una suma de comprobación para una fila, el esquema es desconocido antes del tiempo de ejecución.
  2. calcule la suma de comprobación para todas las filas para obtener la suma de comprobación completa de la tabla.

Respuesta

23

Puede usar CHECKSUM_AGG. Solo se necesita un único argumento, por lo que puede hacer CHECKSUM_AGG(CHECKSUM(*)), pero esto no funciona para su tipo de datos XML, por lo que deberá recurrir a SQL dinámico.

Se podría generar dinámicamente la lista de columnas de INFORMATION_SCHEMA.COLUMNS y luego insertar en una plantilla int:

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(
    '{@column_list}', @column_list), 
    '{@schema_name}', @schema_name), 
    '{@table_name}', @table_name) 

EXEC (@sql) 
+0

Gracias! Tuve que ajustarlo un poco a un caso especial de los tipos de datos, pero pude encontrar algo rápido y basado en esta solución. ¡Excelente! –

+0

Solo tenga cuidado con las columnas de identidad, también usaría BINARY_CHECKSUM ya que es sensible a mayúsculas y minúsculas. –

2

he modificado la secuencia de comandos para generar una consulta para todas las tablas relevantes en una base de datos.

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 
0

// suma de hash rápido de SQL y C# espejo Ucrania // HASH_ZKCRC64 /// ----------------------- -------------------------------------------------- ------------------------------------- privado Int64 HASH_ZKCRC64 (byte [] datos) { Int64 Resultado = 0x5555555555555555; if (Datos == nulo || 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<[email protected] 
       Begin 
       Set @Bufer=Substring(@data,@i,8000) 
       Set @j=1 
        While @j<=8000 
        Begin 
        Set @[email protected] 
        ^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 @[email protected]^CAST(Substring(@Bufer,@j+28,8) as bigint)  
        Set @[email protected]+64  
        End; 
       Set @[email protected]+8000 
       End 
      Return @I64 
      END 
     */ 
     #endregion 

    } 
+1

Formatee su código y actualice su respuesta con una explicación. –

Cuestiones relacionadas