Sql server 通过更改数据捕获和哈希提高合并性能

Sql server 通过更改数据捕获和哈希提高合并性能,sql-server,tsql,merge,dimensional-modeling,Sql Server,Tsql,Merge,Dimensional Modeling,今天,我尝试调整审计数据库的性能。我有跟踪行更改的法律原因,并且我在SQL Server 2016中使用系统版本表方法实现了一组表 我的整个过程将“原始”数据从源系统导入初始表。从这里开始,我就有了一个合并过程,它从原始表中获取数据,并将原始表中的每一列与可审计的系统版本化临时表中存在的列进行比较,并确定发生了哪些更改。然后,系统行版本控制告诉我哪些更改了,哪些没有更改 这种方法的问题是我的表太宽了。其中一些有400列或更多。即使是包含450000条记录的表,SQL server也需要大约17分

今天,我尝试调整审计数据库的性能。我有跟踪行更改的法律原因,并且我在SQL Server 2016中使用系统版本表方法实现了一组表

我的整个过程将“原始”数据从源系统导入初始表。从这里开始,我就有了一个合并过程,它从原始表中获取数据,并将原始表中的每一列与可审计的系统版本化临时表中存在的列进行比较,并确定发生了哪些更改。然后,系统行版本控制告诉我哪些更改了,哪些没有更改

这种方法的问题是我的表太宽了。其中一些有400列或更多。即使是包含450000条记录的表,SQL server也需要大约17分钟来执行合并操作。这确实降低了我们解决方案的性能,如果我们能够加快速度,似乎会有很大帮助。我们目前有数百张桌子需要这样做

目前,原始表和阶段表都在ID列上建立索引

我曾在几个地方阅读过,我们可能会考虑使用校验和或HasByMead函数来记录原始提取中的值。(这叫什么?GUID?UUID?哈希?)。然后,我们将计算值与STAGE表中存在的值进行比较。但问题是:在许多列中经常有相当多的空值。有人建议我们将所有列类型强制转换为相同的(nvarchar(max))?,空值似乎会导致整个校验和计算失败。所以我也在我的代码中编码了很多ISNULL(,'UNKNOWN')语句

那么,这里有没有更好的方法来提高合并的性能呢?我认为我可以使用行更新的timestamp列作为单个值来比较,而不是使用校验和,但我不确定这是否会通过法律审查。Legal担心的是,行可能会在界面之外编辑,而列不会总是更新。我见过开发人员使用串联函数(如下所示)将许多列值组合在一起的方法。这似乎是代码密集型的,而且计算/转换列的成本也很高

因此,我的问题是:

  • 考虑到实际情况,我可以在这里以任何方式提高合并性能吗
  • 我应该使用校验和还是哈希字节,为什么
  • 这里哪个hashbytes方法最有意义?(我只是基于ID匹配将一个原始行与另一个阶段行进行比较,对吗)
  • 我是否遗漏了一些功能,这些功能可能会使阅读中的比较更快或更容易 我做了什么?奇怪的是,在SQLServer中,除了CONCAT之外,没有更好的函数可以实现这一点
  • 我写了下面的代码来展示我正在考虑的一些想法。还有比我在下面写的更好的吗

    DROP TABLE IF EXISTS MyTable;
    
    CREATE TABLE MyTable
        (C1 VARCHAR(10),
         C2 VARCHAR(10),
         C3 VARCHAR(10)
         );
    
    INSERT INTO MyTable
        (C1,C2,C3)
    VALUES
        (NULL,NULL,NULL),
        (NULL,NULL,3),
        (NULL,2,3),
        (1,2,3);
    
    
    SELECT
        HASHBYTES('SHA2_256',
        CONCAT(C1,'-',
               C2,'-',
               C3)) AS HashbytesValueCastWithNoNullCheck,
    
    
        HASHBYTES('SHA2_256',
        CONCAT(CAST(C1 as varchar(max)),'-',
               CAST(C2 as varchar(max)),'-',
               CAST(C3 as varchar(max)))) AS HashbytesValueCastWithNoNullCheck,
    
    
        HASHBYTES('SHA2_256',
        CONCAT(ISNULL(CAST(C1 as varchar(max)),'UNKNOWN'),'-',
               ISNULL(CAST(C2 as varchar(max)),'UNKNOWN'),'-',
               ISNULL(CAST(C3 as varchar(max)),'UNKNOWN'))) AS HashbytesValueWithCastWithNullCheck,
        CONCAT(ISNULL(CAST(C1 as varchar(max)),'UNKNOWN'),'-',
               ISNULL(CAST(C2 as varchar(max)),'UNKNOWN'),'-',
               ISNULL(CAST(C3 as varchar(max)),'UNKNOWN')) AS StringValue,
        CONCAT(C1,'-',C2,'-',C3) AS ConcatString,
        C1,
        C2,
        C3
    FROM
        MyTable;
    
    '''
    
考虑到实际情况,我可以在这里以任何方式提高合并性能吗

您应该进行测试,但是为每一行存储一个哈希,计算新行的哈希,并基于(键,哈希)进行比较应该比比较每一列便宜

我应该使用校验和还是哈希字节,为什么

HASHBYTES丢失更改的概率要低得多。粗略地说,使用校验和,您可能最终会错过一两次更改,而使用HASHBYTES,您可能永远不会错过任何更改。请参阅此处的备注:

在阅读过程中,我是否遗漏了一些可能使比较更快或更容易的功能

没有。没有特殊的方法来比较多个列

还有比我在下面写的更好的吗

DROP TABLE IF EXISTS MyTable;

CREATE TABLE MyTable
    (C1 VARCHAR(10),
     C2 VARCHAR(10),
     C3 VARCHAR(10)
     );

INSERT INTO MyTable
    (C1,C2,C3)
VALUES
    (NULL,NULL,NULL),
    (NULL,NULL,3),
    (NULL,2,3),
    (1,2,3);


SELECT
    HASHBYTES('SHA2_256',
    CONCAT(C1,'-',
           C2,'-',
           C3)) AS HashbytesValueCastWithNoNullCheck,


    HASHBYTES('SHA2_256',
    CONCAT(CAST(C1 as varchar(max)),'-',
           CAST(C2 as varchar(max)),'-',
           CAST(C3 as varchar(max)))) AS HashbytesValueCastWithNoNullCheck,


    HASHBYTES('SHA2_256',
    CONCAT(ISNULL(CAST(C1 as varchar(max)),'UNKNOWN'),'-',
           ISNULL(CAST(C2 as varchar(max)),'UNKNOWN'),'-',
           ISNULL(CAST(C3 as varchar(max)),'UNKNOWN'))) AS HashbytesValueWithCastWithNullCheck,
    CONCAT(ISNULL(CAST(C1 as varchar(max)),'UNKNOWN'),'-',
           ISNULL(CAST(C2 as varchar(max)),'UNKNOWN'),'-',
           ISNULL(CAST(C3 as varchar(max)),'UNKNOWN')) AS StringValue,
    CONCAT(C1,'-',C2,'-',C3) AS ConcatString,
    C1,
    C2,
    C3
FROM
    MyTable;

'''
您肯定应该替换null,否则一行
(1,null,'a')
(1,'a',null)
将得到相同的哈希值。您应该使用不会在任何列中显示为值的内容替换null和定界。如果您有Unicode文本,转换为varchar可能会删除一些更改,因此使用nvarchar更安全。例如:

HASHBYTES('SHA2_256',
    CONCAT(ISNULL(CAST(C1 as nvarchar(max)),N'~'),N'|',
           ISNULL(CAST(C2 as nvarchar(max)),N'~'),N'|',
           ISNULL(CAST(C3 as nvarchar(max)),N'~'))) AS HashbytesValueWithCastWithNullCheck
SQL Server中的JSON非常快。因此,您可以尝试以下模式:

select t.Id, z.RowJSON, hashbytes('SHA2_256', RowJSON) RowHash
from SomeTable t
cross apply (select t.* for json path) z(RowJSON)

Hashbytes就是我这样做的(请注意,即使是SHA2_256也有发生冲突的机会,因此请确保它通过了法律程序,而不仅仅是功能需求)。有没有空间使用Redgate的DataCompare之类的工具?比较工具可能使用相同的底层函数,但我看到它执行得非常快,所以可能它们有额外的算法。另外,您需要在术语之间添加一个分隔符,否则“Jo Nethen”=“Jon Ethen”。您认为如何组合
binary\u checksum()
hashbytes()
来避免
isnull()?POC as
select*,HASHBYTES('SHA2_256',concat(校验和(a,b,c),a,b,c)),from(值(1,'a',null),(1,null,'a')作为x(a,b,c)这很聪明,但我看不出有办法估计碰撞的概率。请参阅答案更新以获取备选答案。