如何在 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)

我想合并ValuesTarget以下规则:

  • 匹配 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)

所以...

这是我到目前为止所拥有的,它负责updateinsert

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 语句

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