MERGE 最佳实践的说明

Sco*_*ain 6 sql-server best-practices t-sql sql-server-2012 merge

我只是想从“优化 MERGE 语句性能”页面。

我正在使用一个数据仓库,它从许多不同的数据库中获取记录并存储数据。我的仓库数据库中的所有表基本上都遵循相同的模式:

CREATE TABLE Foo (
    database_guid UNIQUEIDENTIFIER
    ,FooPk BIGINT
    ,Bar NVARCHAR(20)
    ,Qix NCHAR(10)
    ,CONSTRAINT [PK_Foo] PRIMARY KEY CLUSTERED (
        database_guid ASC
        ,FooPk ASC
        )
    )
GO

CREATE PROCEDURE [iv].[LoadSomeTable] 
    @databaseGUID UNIQUEIDENTIFIER
AS
BEGIN
    SET NOCOUNT ON

    MERGE Foo
    USING #FooStaging AS Source
    ON Foo.FooPk = Source.FooPk AND Foo.database_guid = @databaseGUID
    WHEN MATCHED THEN
        UPDATE SET Bar = Source.Bar
                  ,Qix = Source.Qix
    WHEN NOT MATCHED THEN
        INSERT (database_guid, FooPk, Bar, Qix)
            VALUES (@databaseGUID, FooPk, Bar, Qix);
END
GO

CREATE TABLE #FooStaging (
    FooPk BIGINT
    ,Bar NVARCHAR(20)
    ,Qix NCHAR(10)
    )

--Data gets loaded in to #FooStaging from a C# call to SqlBulkCopy then calls iv.LoadSomeTable
Run Code Online (Sandbox Code Playgroud)

我现在担心的是我刚刚从那个 MSDN 页面上读到了这个声明

仅在 ON<merge_search_condition> 子句中指定用于确定匹配源表和目标表中数据的条件的搜索条件。也就是说,仅指定目标表中与源表的相应列进行比较的列。不包括与其他值(如常量)的比较。

读完之后我想我的查询错了,我的合并语句应该是

MERGE Foo
USING #FooStaging AS Source
ON Foo.FooPk = Source.FooPk
WHEN MATCHED AND Foo.database_guid = @databaseGUID THEN
    UPDATE SET Bar = Source.Bar
              ,Qix = Source.Qix
WHEN NOT MATCHED THEN
    INSERT (database_guid, FooPk, Bar, Qix)
        VALUES (@databaseGUID, FooPk, Bar, Qix);
Run Code Online (Sandbox Code Playgroud)

但这对我来说并不“感觉”正确,因为该database_guid字段是主键的一部分,所以它不应该包含在on? 如果我在有它WHEN MATCHED,我上传一个数据库,一个FooPk1话,我上传第二个数据库与FooPk和不同的@databaseGUID我不知道,如果NOT MATCHED会触发与否(只是测试,事实并非如此)。

哪种方式是使用 MERGE 的正确方法?

Aar*_*and 9

我敢说你最好的方法是对每个潜在的动作使用单独的语句,并将它们放在一个可序列化的事务中。您可以使用经过验证的真实语句,没有有趣的语义或“最佳实践”违规,并且可以避免我在本文中概述的所有问题,包括错误结果错误和潜在的索引损坏:

  • @ScottChamberlain:请参阅 [this](http://stackoverflow.com/a/1106934/579117) 答案。 (4认同)