【问题标题】:MERGE with DELETE on target with partial match on source?在目标上与 DELETE 合并,在源上部分匹配?
【发布时间】:2018-02-15 04:51:36
【问题描述】:

假设我有一个带有复合主键的表:

create table FooBar (
   fooId int not null,
   barId int not null,
   status varchar(20) not null,
   lastModified datetime not null, 
   constraint PK_FooBar primary key (fooId, barId)
);

现在我有一些特定 fooId 的表格数据,可能是这样的:

1, 1, 'ACTIVE'
1, 2, 'INACTIVE'

...我想创建一个 MERGE 语句,将此表格数据视为权威仅适用于 fooId 1,删除 FooBar 中适用于 @987654327 的任何不匹配记录@ 1,但将所有记录保留为 fooId,这 不是 1。

例如,假设FooBar 表当前有以下数据:

1, 1, 'ACTIVE', ... (some date, not typing it out)
2, 1, 'ACTIVE', ...
1, 3, 'INACTIVE', ...
2, 2, 'INACTIVE'

我想对上面提到的两个数据集运行一个合并语句,FooBar 中的结果数据集应该如下所示:

1, 1, 'ACTIVE', ...
2, 1, 'ACTIVE', ...
1, 2, 'INACTIVE', ...
2, 2, 'INACTIVE', ...

我希望删除1, 3, 'INACTIVE' 行,并使用新修改的时间戳更新1, 1, 'ACTIVE' 行,并插入1, 2, 'INACTIVE' 行。我还希望 fooId 的 2 记录保持不变。

这可以在一个语句中完成吗?如果有,怎么做?

【问题讨论】:

  • 通常,合并可以从源中插入目标中不存在的行,更新(源和目标)中都存在的行并删除源不匹配的内容(也将此调整为 foodId = 1 可以完成)但也许您应该提供源、目标和预期结果的示例数据以获得更好的答案。也许this thread 有帮助。

标签: sql-server merge upsert


【解决方案1】:

您可以将common table expressions 用于您的目标和源,并且在这些 ctes 中您可以应用过滤器来定义您想要使用的行:

-- t = Target = Destination Table = Mirror from Source
-- s = Source = New Data source to merge into Target table 

declare @FooId int = 1;

;with t as (
  select fooId, barId, [status], lastModified 
  from dbo.FooBar f 
  where f.fooId = @FooId
  )
,  s as (
  select fooId, barId, [status], lastModified=sysutcdatetime() 
  from src 
  where src.fooId = @FooId
  )
merge into t with (holdlock) -- holdlock hint for race conditions
using s 
  on (s.FooId = t.FooId and s.barId = t.barId)

    /* If the records matches, update status and lastModified. */
    when matched 
      then update set t.[status] = s.[status], t.lastModified = s.lastModified

    /* If not matched in table, insert the record */
    when not matched by target
      then insert (fooId,  barId,   [status],  lastModified)
        values (s.fooId, s.barId, s.[status], s.lastModified)

    /* If not matched by source, delete the record*/
    when not matched by source
      then delete
 output $action, inserted.*, deleted.*;

rextester 演示:http://rextester.com/KRAI9699

返回:

+---------+-------+-------+----------+---------------------+-------+-------+----------+---------------------+
| $action | fooId | barId |  status  |    lastModified     | fooId | barId |  status  |    lastModified     |
+---------+-------+-------+----------+---------------------+-------+-------+----------+---------------------+
| INSERT  | 1     | 2     | INACTIVE | 2017-09-06 16:21:21 | NULL  | NULL  | NULL     | NULL                |
| UPDATE  | 1     | 1     | ACTIVE   | 2017-09-06 16:21:21 | 1     | 1     | ACTIVE   | 2017-09-06 16:21:21 |
| DELETE  | NULL  | NULL  | NULL     | NULL                | 1     | 3     | INACTIVE | 2017-09-06 16:21:21 |
+---------+-------+-------+----------+---------------------+-------+-------+----------+---------------------+

merge参考:

【讨论】:

  • 这样不会去掉目标表中fooId2的记录吗?
  • @JeremyHolovacs 不,不会。 --查看演示。在t 的公用表表达式中,where f.fooId = @FooId 将其限制为fooId = 1 所在的行。只有这些行是merge 操作的一部分。
  • ...这太酷了,说不出话来。我不知道 CTE 可以这样使用。
  • @JeremyHolovacs 乐于助人!我在merge上加了一些推荐读物,使用时有很多注意事项。
猜你喜欢
  • 1970-01-01
  • 2020-01-20
  • 2020-01-14
  • 1970-01-01
  • 2012-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-29
相关资源
最近更新 更多