2012-08-08 7 views
12

Esiste un modo in SQL Server 2012 per generare un hash di un set di righe e colonne?Generare un hash per un set di righe nel server sql

Voglio generare un hash, memorizzarlo nel record padre. Quando arriva un aggiornamento, confronterò l'hash in arrivo con l'hash del record padre e saprò se i dati sono cambiati.

Quindi qualcosa di simile sarebbe bello:

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

"concatenate" sarebbe una funzione di aggregazione che non solo concat le colonne, ma anche, le righe all'interno del gruppo di risultati. Come MAX, ma restituire tutto come una concatenazione di stringhe.

Speriamo che questo ti aiuti a vedere cosa intendo comunque!

Il problema fondamentale che sto cercando di risolvere è che il sistema del mio cliente esegue importazioni di grandi quantità di dati gerarchici. Se riesco a evitare l'elaborazione tramite l'uso degli hash, allora penserei che questo risparmierà molto tempo. Al momento, l'SP sta rallentando del 300% quando si devono elaborare dati duplicati.

Grazie

risposta

10

È possibile utilizzare la CHECKSUM_AGG aggregato. è fatto per quello scopo.

+4

Sfortunatamente CHECKSUM ha conosciuto punti deboli (cioè collisioni pratiche). Per esempio. tipo decimale http://sqlserverpains.blogspot.com.au/2008/06/checksum-pains.html quindi fai attenzione. – Shiv

1

singola di hash fila:

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

per la tavola checksum:

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

Questo produrre un hash dall'intero set di risultati? O produrrà più hash, uno per ogni riga in MyChildTable? – krisdyson

+0

prova la seconda soluzione nella mia modifica. –

+0

Ho aggiornato di nuovo per evitare un overflow di interi. –

1

Un altro approccio:

-- 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))) 

ma HashBytes è limitata a soli 8000 byte ... si può fare una funzione per ottenere de MD5 per ogni 8000 byte ....

+0

Se si utilizza SQL Server 2016 o versione successiva, che dispone di supporto JSON, si consiglia di utilizzare 'FOR JSON AUTO' anziché' FOR XML AUTO', in quanto sembra essere circa 2 volte più veloce in alcuni test effettuati. – Isak

Problemi correlati