2012-08-08 17 views
12

¿Hay alguna forma en SQL Server 2012 para generar un hash de un conjunto de filas y columnas?Generar un hash para un conjunto de filas en el servidor sql

Quiero generar un hash, almacenarlo en el registro principal. Cuando llegue una actualización, compararé el hash entrante con el hash de registro principal y sabré si los datos han cambiado.

Así que algo como esto sería bueno:

SELECT GENERATEHASH(CONCATENATE(Name, Description, AnotherColumn)) 
FROM MyChildTable WHERE ParentId = 2 -- subset of data belong to parent record 2 

"concatenar" sería una función agregada lo que no sólo concat las columnas, sino también, dentro de las filas del conjunto de resultados. Como MAX, pero devolviendo todo como una concatenación de cadenas.

Espero que esto te ayude a ver lo que quiero decir de todos modos!

El problema fundamental que trato de resolver es que el sistema de mi cliente realiza importaciones de grandes cantidades de datos jerárquicos. Si puedo evitar el procesamiento mediante el uso de hashes, entonces creo que esto ahorrará mucho tiempo. Por el momento, el SP se está ejecutando un 300% más lento cuando tiene que procesar datos duplicados.

Muchas gracias

Respuesta

10

Usted puede utilizar el agregado CHECKSUM_AGG. está hecho para ese propósito.

+4

Desafortunadamente CHECKSUM tiene debilidades conocidas (es decir, colisiones prácticas). P.ej. tipo decimal http://sqlserverpains.blogspot.com.au/2008/06/checksum-pains.html así que ten cuidado. – Shiv

1

Para los hash de una sola fila:

select HASHBYTES('md5', Name + Description + AnotherColumn) 
FROM MyChildTable WHERE ParentId = 2 

de suma de comprobación tabla:

select sum(checksum(Name + Description + AnotherColumn)*1.0) 
FROM MyChildTable WHERE ParentId = 2 
+0

hace esto producir un hash de todo el conjunto de resultados? ¿O producirá hashes múltiples, uno para cada fila en MyChildTable? – krisdyson

+0

intente con la segunda solución en mi edición. –

+0

He vuelto a actualizar para evitar un desbordamiento de enteros. –

1

Otro enfoque:

-- compute a single hash value for all rows of a table 
begin 

    set nocount on; 

    -- init hash variable 
    declare @tblhash varchar(40); 
    set @tblhash = 'start'; 

    -- compute a single hash value 
    select @tblhash = sys.fn_varbintohexsubstring(0, hashbytes('sha1',(convert(varbinary(max),@tblhash+ 
    (select sys.fn_varbintohexsubstring(0,hashbytes('sha1',(convert(varbinary(max), 
    -- replace 'select *' if you want only specific columns to be included in the hash calculation 
    -- [target table] is the name of the table to calc the hash from 
    -- [row_id] is the primary key column within the target table 
    -- modify those in the next lines to suit your needs: 
    (select * from [target_table] obj2 where obj2.[row_id]=obj1.[row_id] for xml raw) 
    ))),1,0)) 
    ))),1,0) 
    from [target_table] obj1; 

    set nocount off; 

    -- return result 
    select @tblhash as hashvalue; 

end; 
9
select HashBytes('md5',convert(varbinary(max),(SELECT * FROM MyChildTable WHERE ParentId = 2 FOR XML AUTO))) 

pero HashBytes está limitada a sólo 8000 bytes ... se puede hacer una función para obtener de Md5 por cada 8000 bytes ....

+0

Si está en SQL Server 2016 o superior, que tiene compatibilidad con JSON, le recomiendo usar 'FOR JSON AUTO' en lugar de' FOR XML AUTO', ya que parece ser aproximadamente 2 veces más rápido en algunas pruebas que hice. – Isak

Cuestiones relacionadas