SQL Server可以单独执行基于哈希的行相等(用于MERGE操作)吗?

时间:2018-10-14 06:21:17

标签: sql-server

我正在开发的系统使用SQL Server将查询结果存储在表中。行组代表特定日期(DataDate)的数据快照,该快照最终随着新数据替换而过期。该表保留旧数据而不是覆盖它。这是一个简单的示例:

CREATE TABLE query1234Results (
    -- Metadata columns:
    AccountId   int      NOT NULL,
    DataDate    date     NOT NULL,
    Appeared    datetime NOT NULL,
    Disappeared datetime     NULL,
    Updated     datetime NOT NULL,

    -- Query result columns:
    TotalSales                money,
    NewCustomers              int,
    CountHovercraftFullOfEels float,
    SumScratchedTobacconists  decimal
    -- etc...

    PRIMARY KEY ( AccountId, DataDate, Appeared )
)
  • AccountId是因为这是一个多租户系统。
  • Appeared是显示结果的日期时间。
  • Disappeared是结果停止出现在数据结果中的日期时间。
  • DataDate是该行对应的日期。
  • Updated是记录的当前状态最后一次得到确认的日期时间。

应用程序收到该表的数据时,会使用MERGE这样的查询将其插入到表中;

MERGE query1234Results AS t
USING @tableValuedParamter AS s ON
    t.AccountId = s.AccountId AND
    t.DataDate  = s.DataDate  AND
    t.Disappeard IS NULL AND

    t.TotalSales   = s.TotalSales AND
    t.NewCustomers = s.NewCustomers AND
    t.CountHovercraftFullOfEels = s.CountHovercraftFullOfEels AND
    t.SumScratchedTobacconists = s.SumScratchedTobacconists,
    -- etc

WHEN MATCHED THEN UPDATE SET
    t.Updated = GETUTCDATE()

WHEN NOT MATCHED BY TARGET THEN
    INSERT (
        AccountId,
        Appeared,
        Disappeared,
        Updated,

        TotalSales,
        NewCustomers,
        CountHovercraftFullOfEels,
        SumScratchedTobacconists,
        -- etc
    )
    VALUES (
        s.AccountId,
        s.Appeared,
        NULL,
        GETUTCDATE(),

        s.TotalSales,
        s.NewCustomers,
        s.CountHovercraftFullOfEels, 
        s.SumScratchedTobacconists,
        -- etc
    )

WHEN NOT MATCHED BY SOURCE AND
    t.AccountId = s.AccountId AND
    t.Disappeared IS NULL

    THEN UPDATE SET
        t.Disappeared = GETUTCDATE(),
        t.Updated     = GETUTCDATE()

该设计适用于小型数据集,但是当MERGE查询需要INSERT超过100,000条记录(即单个AccountId的数据需要100,000天)时以及当表有30多个查询结果列,它遇到了您期望的性能问题。

部分问题是,SQL Server需要对每个非元数据列执行相等性检查,以便确定它是否应该INSERT数据还是UPDATE数据。

显然,可以通过添加正确的索引来改善此问题,但是我觉得我需要添加一个包含每个非元数据列的索引,以便快速运行MERGE比较。我认为这种方法无法很好地扩展。

存在一种解决方法,使用值散列策略为所有非元数据值计算散列,并且仅对MERGE连接条件使用散列,如下所示:

  1. 添加另一个元数据列Hash bigint,该列存储从要成为MERGE的数据的非元数据列中计算出的64位MurmurHashV2。
  2. MERGE更改为此:

    MERGE query1234Results AS t
    USING @tableValuedParamter AS s ON
        t.AccountId = s.AccountId AND
        t.DataDate  = s.DataDate  AND
        t.Disappeard IS NULL      AND
        t.Hash      = s.Hash
    
    WHEN MATCHED...
    

存在散列冲突风险的问题,因此MERGE查询仍必须对所有值字段进行比较-但SQL并未提供短路AND / {{ 1}}运算符,所以我不知道如何指示SQL Server仅在哈希比较成功的情况下才进行昂贵的值比较。

我想这实际上是在行本身中包含值的散列,这违反了关注点分离:散列值应存储在DBMS维护的某个索引中,并且只有DBMS才应关注自身而不是我的应用程序逻辑。

...但是我知道,如果我确实在所有非元数据列上创建索引,则该索引将创建一个基于常规B树的索引,该索引必定会很大,因为它将存储源表中的每个唯一值。

我的问题:SQL Server是否有一种方法可以创建基于哈希的索引来优化多列相等性检查,而无需将此哈希逻辑并入我的应用程序域中?如果不是,我建议的将哈希存储在表中的方法是否是最好的替代方法?

1 个答案:

答案 0 :(得分:1)

  

部分问题是SQL Server需要对每个非元数据列执行相等性检查,以便确定是否应插入数据或更新数据。

在我回答其余问题之前,我想确保这确实是问题所在。您如何确定相等比较的数目是问题?我个人很难确定这一点。您可以通过删除大多数比较并在不需要条件的情况下使用测试数据来进行测试。

我怀疑这不是问题。

尽管我无法告诉您SQL Server是否要短路这些AND条件(but SQL doesn't provide for short-circuiting AND/OR operators),但我怀疑是这样。 AND的规范没有讨论评估,但是没有评估。它仅讨论语义。语义不允许您区分是否存在短路。因此,SQL Server可以执行此优化。优化似乎是一种常见情况。

当然,如果数据足以必须评估大多数条件,则短路可能无济于事。但是我再次怀疑这一点尚无定论。

  

显然,可以通过添加正确的索引来改善这一点

添加一个足够具体的索引就足够了,以便大多数比较由该索引处理。索引的目的是避免二次检查成本(每个源行相对于每个目标行)。添加所有其他列将无助于性能。 SQL Server相等性是在索引操作期间进行比较还是在后续的筛选步骤中进行比较,并不重要。也许过滤器甚至更快(?)。

  

存在一种解决方法,使用值散列策略为所有非元数据值计算散列,仅对MERGE连接条件使用散列,如下所示:

如果您发现这些比较确实影响了性能,则此策略可能有意义。如果您期望哈希在大多数情况下不匹配,请先进行比较。由于短路,所有其他比较可能会消失。如果散列在大多数时间都匹配,那么它将无济于事,因为无论如何都必须评估其他比较。您可以使用加密哈希,这样就不必检查每一列。出于实际考虑,加密散列是完全可靠的。

  

这让我认为实际上将值的散列包含在行本身中是对关注点分离的违反

如果它对您有用,就可以了。这确实是唯一重要的标准。不要基于这样的总括规则来决定。考虑到每个问题,权衡一下,然后决定研究整个解决方案。

  

SQL Server是否可以创建基于散列的索引

Hekaton具有基于哈希的索引。这会起作用。但这也意味着每个查询都必须对源数据进行哈希处理以进行匹配。您无法对其进行预先计算。