Get the SHA-512 of any SQL query

2019-08-05 00:54发布

问题:

A common practice to compare two text file is to use the SHA-512 [or any other realted SHA algo]. If two SHA results are not the same then the files are not exactely the same.

I would like to do the same with two SQL queries. I just want to know if the queries gives 100% identical result or not with a SHA-512 [or sha-256 would be OK as well]?

Is that possible to perform that? I am using SQL Server...

回答1:

Just to help...

It's understood that both queries return the same columns in the same order.

You must do:

SELECT COUNT(*) FROM (
  ([YOUR_QUERY_A]
   EXCEPT
   [YOUR_QUERY_B]) -- A_B
  UNION ALL
  ([YOUR_QUERY_B]
   EXCEPT
   [YOUR_QUERY_A]) -- B_A
  ) EX

If returns 0, both queries return the same

For test purposes:

SELECT COUNT(*) FROM (
  (select 1 a
   EXCEPT
   select 1)
  UNION ALL
  (select 1
   EXCEPT
   select 1)
  ) EX

Change some inner query and look what changes



回答2:

Here's some example SQL that uses 2 methods.

  1. FOR XML & HASHBYTES
  2. EXCEPT
IF OBJECT_ID('tempdb..#tmpTest') IS NOT NULL DROP TABLE #tmpTest;
CREATE TABLE #tmpTest (
    id int identity(1,1) primary key, 
    col1 decimal(10,2), 
    col2 varchar(30)
);

insert into #tmpTest (col1, col2) values 
(1,'val1'),
(null,'val2'),
(3,null),
(4,'val4')
-- ,(5,'monkeywrench')
;

declare @SQL1 VARCHAR(1000);
declare @SQL2 VARCHAR(1000);
declare @SQLHASH VARCHAR(3000);
declare @SQLEXCEPT VARCHAR(5000);

set @SQL1 = 'select col1, col2 
from #tmpTest
where (col1 is null or col1 between 1 and 4)
';

set @SQL2 = 'select col1, col2 
from #tmpTest
where (col2 is null or col2 is not null)
';

set @SQLHASH = 'select 
IIF(LEN(query1.x) = LEN(query2.x) AND HASHBYTES(''SHA2_512'', query1.x) = HASHBYTES(''SHA2_512'', query2.x),''true'',''false'') as SameHash
from (
    '+ @SQL1 +'
    order by 1, 2
    for xml auto
) query1(x)
cross join (
    '+ @SQL2 +'
    order by 1, 2
    for xml auto
) query2(x)';

--select @SQLHASH as SQLHASH;
execute(@SQLHASH);

set @SQLEXCEPT = 'select 
IIF(count(*) = 0,''true'',''false'') as SameRecords
from (
    select * from
    (
        '+ @SQL1 +'
        except
        '+ @SQL2 +'
    ) as q1exceptq2

    union all

    select * from (
        '+ @SQL2 +'
        except
        '+ @SQL1 +'
    ) as q2exceptq1
) q';

--select @SQLEXCEPT as SQLEXCEPT;
execute(@SQLEXCEPT);

In this example, both dynamic queries return 'true'.

But do note that just because the result sets are the same, doesn't mean that the criteria used are equivalent.
It might just be bad luck that they are currently returning the same results.
(just uncomment the monkeywrench record to get false from both)

Also, about the FOR XML. When one of the ORDER BY is different, then the resulting XML and HASH will also be different.

While with an EXCEPT you can only add an ORDER BY at the end, because it sorts the combined resultset.