合并 - 条件“匹配时”输出

问题描述 投票:2回答:1

以下代码显示了两个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子句。有没有更好的方法?

tsql merge sql-server-2014
1个回答
1
投票

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

根据在线书籍:Merge(强调我的)

匹配然后<merge_matched> 指定* target_table的所有行(与ON返回的行匹配,并满足任何其他搜索条件)根据子句进行更新或删除。

MERGE语句最多可以有两个WHEN MATCHED子句。如果指定了两个子句,则第一个子句必须附带AND子句。对于任何给定的行,仅当第一个不是时才应用第二个WHEN MATCHED子句。如果有两个WHEN MATCHED子句,则必须指定UPDATE操作,并且必须指定DELETE操作。当在子句中指定UPDATE,并且多个匹配的行匹配target_table中的一行时,SQL Server将返回错误。 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

它向我展示了错误

Msg 10714,Level 15,State 1,Line 33类型'WHEN MATCHED'的行为在MERGE声明的'UPDATE'条款中不能出现多次。

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

© www.soinside.com 2019 - 2024. All rights reserved.