合并 - 有条件的"匹配" OUTPUT

时间:2017-01-26 10:13:20

标签: tsql merge sql-server-2014

以下代码显示了两个WHEN MATCHED - 子句。 第一个匹配时只有DATETIME - 列' updatedAt'改变了。 这应该更新目标,但我不希望在OUTPUT中标记这一点。第二个匹配在给定匹配中有其他更改时,这应该导致OUTPUT

MERGE [Target].dbo.[clients] AS target
USING [Source].dbo.[clients] AS source
ON target.[objectId]=source.[objectId]
WHEN MATCHED AND NOT EXISTS (
                    SELECT source.firstName, ...
                    EXCEPT
                    SELECT target.firstName, ...
                ) AND source.updatedAt <> target.updatedAt
THEN 
    UPDATE SET --THIS UPDATE SHOULD NOT LEAD TO AN OUTPUT WITH $ACTION = 'UPDATE'
        target.updatedAt = source.updatedAt
WHEN MATCHED AND EXISTS (
                    SELECT source.firstName, ... , source.updatedAt
                    EXCEPT
                    SELECT target.firstName, ... , target.updatedAt
                )
THEN
    UPDATE SET --THIS UPDATE SHOULD LEAD TO AN OUTPUT WITH $ACTION = 'UPDATE'
        target.[firstName]=source.[firstName], ...
WHEN NOT MATCHED BY TARGET
THEN
    INSERT ([objectId],[firstName], ... ,[updatedAt]) VALUES ([objectId],[firstName], ... ,[updatedAt])
WHEN NOT MATCHED BY SOURCE
THEN
    DELETE

OUTPUT 
    $ACTION ChangeType
    , ISNULL(
        inserted.objectId
        , deleted.objectId
    ) AS objectId
    , GETDATE() AS DateTimeChanged
;

这可以通过输出所有源列和目标列并将MERGE放在子查询中来实现,以便在主查询中进行比较,例如:

WHERE NOT (
    [ChangeType]='UPDATE' 
    AND [src objectId]=[tgt objectId] 
    AND [src firstName]=[tgt firstName] 
    AND ... 
    AND [src updatedAt]<>[tgt updatedAt]
)

然而,我觉得应该有更好的方法,因为我能够宣布两个不同的WHEN MATCHED - 条款。还有更好的办法吗?

1 个答案:

答案 0 :(得分:0)

我认为您的示例不正确,因为您有两个条件WHEN MATCHED子句。

根据在线图书:Merge(强调我的意思)

  

然后匹配时
   指定的所有行   * target_table,它匹配ON返回的行,并满足任何其他搜索条件,   根据该条款进行了更新或删除。

     

MERGE语句最多可以具有两个子句。如果   指定了两个子句,第一个子句必须带有一个   AND子句。对于任何给定的行,第二个WHEN   仅在第一个条件不适用时才应用MATCHED子句。   使用MATCHED子句时,一个必须指定一个UPDATE动作,另一个必须   请指定DELETE操作。    子句,以及超过一行   根据SQL匹配target_table中的一行   服务器返回错误。 MERGE语句无法更新同一行   不止一次,或者更新和删除同一行。

我也尝试过您的代码

BEGIN TRANSACTION
SET XACT_ABORT ON;

CREATE TABLE TargetClients
(
    objectId BIGINT
    , firstName VARCHAR(50)
    , updatedAt DATETIME2(0)

)

CREATE TABLE SourceClients
(
    objectId BIGINT
    , firstName VARCHAR(50)
    , updatedAt DATETIME2(0)

)

go

MERGE TargetClients AS target
USING SourceClients AS source
ON target.[objectId]=source.[objectId]
WHEN MATCHED AND NOT EXISTS (
                    SELECT source.firstName
                    EXCEPT
                    SELECT target.firstName
                ) AND source.updatedAt <> target.updatedAt
THEN 
    UPDATE SET --THIS UPDATE SHOULD NOT LEAD TO AN OUTPUT WITH $ACTION = 'UPDATE'
        target.updatedAt = source.updatedAt
WHEN MATCHED AND EXISTS (
                    SELECT source.firstName, source.updatedAt
                    EXCEPT
                    SELECT target.firstName, target.updatedAt
                )
THEN
    UPDATE SET --THIS UPDATE SHOULD LEAD TO AN OUTPUT WITH $ACTION = 'UPDATE'
        target.[firstName]=source.[firstName]
WHEN NOT MATCHED BY TARGET
THEN
    INSERT ([objectId],[firstName], [updatedAt]) VALUES ([objectId],[firstName], [updatedAt])
WHEN NOT MATCHED BY SOURCE
THEN
    DELETE

OUTPUT 
    $ACTION ChangeType
    , ISNULL(
        inserted.objectId
        , deleted.objectId
    ) AS objectId
    , GETDATE() AS DateTimeChanged
;



rollback

它向我显示了错误

  

消息10714,第15级,状态1,第33行:类型为“匹配时”的操作   不能在MERGE的'UPDATE'子句中出现多次   声明。

一种可能的解决方案是在Merge_matched子句中使用CASE语句,并使用一个额外的列作为标志。