如何在 SQL Server 中的多键 MERGE 中仅删除相关记录?

Mic*_*ren 7 sql-server sql-server-2012 merge

假设你有这样的事情:

源表(变量):

Values (
  LeftId INT NOT NULL,
  RightId INT NOT NULL,
  CustomValue varchar(100) NULL
)
Run Code Online (Sandbox Code Playgroud)

目标表:

Mapping (
  LeftId INT NOT NULL,
  RightId INT NOT NULL,
  CustomValue varchar(100) NULL
)
Run Code Online (Sandbox Code Playgroud)

我想合并Values成Target以下规则:

  • 匹配 source.LeftId = target.LeftId AND source.RightId = target.RightId
    • 当与目标匹配时,更新 CustomValue
    • 当与目标不匹配时,插入
  • 在目标删除任何不匹配的值不匹配LeftId源,即只能删除记录中涉及到LefId什么,我合并的秒。

(最后一条规则很难描述,抱歉!)

例如:

来源:

1, 10, foo
1, 11, foo
Run Code Online (Sandbox Code Playgroud)

目标:

1, 10, bar
1, 12, foo
2, 20, car
Run Code Online (Sandbox Code Playgroud)

合并结果:

结果目标:

1, 10, foo (updated)
1, 11, foo (inserted)
1, 12, foo (deleted)
2, 20, car (unchanged)
Run Code Online (Sandbox Code Playgroud)

所以...

这是我到目前为止所拥有的,它负责update和insert:

MERGE Mapping AS target
USING (SELECT LeftId, RightId, CustomValue FROM @Values) 
  AS source (LeftId, RightId, CustomValue)
  ON target.LeftId = source.LeftId
    AND target.RightId = source.RightId
WHEN NOT MATCHED THEN
  INSERT (LeftId, RightId, CustomValue)
  VALUES (source.LeftId, source.RightId, source.CustomValue)
WHEN MATCHED THEN
  UPDATE SET
    CustomValue = source.CustomValue;
Run Code Online (Sandbox Code Playgroud)

我如何做我的delete规则的一部分?

Mik*_*son 10

您可以从 CTE 中的目标表中筛选出您需要考虑的行,并使用 CTE 作为合并中的目标。

WITH T AS
(
  SELECT M.LeftId, 
         M.RightId, 
         M.CustomValue
  FROM @Mappings AS M
  WHERE EXISTS (SELECT *
                FROM @Values AS V
                WHERE M.LeftId = V.LeftId) 
)
MERGE T
USING @Values AS S
ON T.LeftId = S.LeftId and
   T.RightId = S.RightId
WHEN NOT MATCHED BY TARGET THEN
  INSERT (LeftId, RightId, CustomValue) 
  VALUES (S.LeftId, S.RightId, S.CustomValue)
WHEN MATCHED THEN
  UPDATE SET CustomValue = S.CustomValue
WHEN NOT MATCHED BY SOURCE THEN
  DELETE
;
Run Code Online (Sandbox Code Playgroud)

  • 为此使用 CTE [显然在 BOL 中被警告](http://dba.stackexchange.com/questions/30633/merge-a-subset-of-the-target-table/30653#30653) 虽然我是不清楚什么时候它的行为可能与视图不同。 (2认同)

Aar*_*and 6

这是DELETE我想到的单独操作:

DELETE m
FROM dbo.Mapping AS m
WHERE EXISTS 
  (SELECT 1 FROM @Values WHERE LeftID = m.LeftID)
AND NOT EXISTS 
  (SELECT 1 FROM @Values WHERE LeftID = m.LeftID AND RightID = m.RightID);
Run Code Online (Sandbox Code Playgroud)

正如我在这里概述的那样,对于左反半连接,该NOT EXISTS模式通常会胜过该LEFT JOIN / NULL模式(但您应该始终进行测试)。

不确定您的总体目标是清晰度还是性能,因此只有您才能判断这是否比NOT MATCHED BY source选项更符合您的要求。您必须定性地查看计划,以及定量地查看计划和/或运行时指标,才能确定。

如果您希望您的MERGE命令能够保护您免受多个独立语句可能发生的竞争条件的影响,您最好通过将其更改为:

MERGE dbo.Mapping WITH (HOLDLOCK) AS target
Run Code Online (Sandbox Code Playgroud)

(摘自Dan Guzman 的博客文章。)

就个人而言,我会做这一切没有MERGE,因为还有未解决的错误,其他原因。并且Paul White 似乎也推荐单独的 DML 语句。

这就是我添加架构前缀的原因:在创建、影响等时,您应该始终按架构引用对象。