以下代码显示了两个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
子句。有没有更好的方法?
我不认为你的例子是正确的,因为你有两个条件的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语句,并使用额外的列作为标志。