【问题标题】:T-SQL: How to merge two tables with delete and insert when matched and only insert when not matched?T-SQL:如何合并两个表,匹配时删除和插入,不匹配时只插入?
【发布时间】:2015-07-23 06:11:45
【问题描述】:

这是一个解释我的意思的示例语句:

DECLARE @sourceTable table(ID int, tmstmp datetime, data varchar(max))
DECLARE @targetTable table(ID int, tmstmp datetime, data varchar(max))

INSERT INTO
    @sourceTable
VALUES
     (1, '2015-07-23T01:01:00', 'Testdata6')
    ,(1, '2015-07-23T02:02:00', 'Testdata7')
    ,(2, '2015-07-23T03:03:00', 'Testdata8')
    ,(2, '2015-07-23T04:04:00', 'Testdata9')

INSERT INTO
    @targetTable
VALUES
     (2, '2015-07-23T00:01:00', 'Testdata1')
    ,(2, '2015-07-23T00:02:00', 'Testdata2')
    ,(2, '2015-07-23T00:03:00', 'Testdata3')
    ,(3, '2015-07-23T00:04:00', 'Testdata4')
    ,(3, '2015-07-23T00:05:00', 'Testdata5')

MERGE INTO
    @targetTable T
USING
    @sourceTable S
ON
    S.ID = T.ID
WHEN MATCHED THEN
    DELETE
    -- also want to INSERT newer ID 2 source records here after delete
WHEN NOT MATCHED THEN
    INSERT (ID, tmstmp, data)
    VALUES (S.ID, S.tmstmp, S.data)
;

当我做出选择时...

SELECT
    *
FROM
    @targetTable

...我得到下表:

ID  tmstmp                  data
3   2015-07-23 00:04:00.000 Testdata4
3   2015-07-23 00:05:00.000 Testdata5
1   2015-07-23 01:01:00.000 Testdata6
1   2015-07-23 02:02:00.000 Testdata7

但我想获得下表:

ID  tmstmp                  data
3   2015-07-23 00:04:00.000 Testdata4
3   2015-07-23 00:05:00.000 Testdata5
1   2015-07-23 01:01:00.000 Testdata6
1   2015-07-23 02:02:00.000 Testdata7
2   2015-07-23 03:03:00.000 Testdata8
2   2015-07-23 04:04:00.000 Testdata9

如何在一个语句中实现这一点,因为我对源表使用了广泛的 CTE。

提前谢谢...

【问题讨论】:

  • 如何从 CTE 切换到将结果存储在具有有意义索引的临时表中?你没有选择?

标签: sql-server merge


【解决方案1】:

我们可以在“源”表中添加一些额外的行来清除现有行,然后让所有当前行落入NOT MATCHED 子句中,这是唯一允许执行@987654323 的子句@操作:

;With Clears as (
  SELECT *,0 as Rem from @sourceTable
  union all
  select distinct ID,'1900-01-01','',1 from @sourceTable
)
MERGE INTO
    @targetTable T
USING
    Clears S
ON
    S.ID = T.ID and s.Rem = 1
WHEN MATCHED THEN
    DELETE
WHEN NOT MATCHED and Rem = 0 THEN
    INSERT (ID, tmstmp, data)
    VALUES (S.ID, S.tmstmp, S.data)
;

Fiddle

尝试在MERGE 语句中实现多个操作的基本规则是,对于要执行的每个操作,您至少需要一个源行。然后,制定ON 子句和WHEN 子句之后的各种附加条件是一个挑战,以便每个操作在您需要时应用。

例如如果没有额外的and Rem = 0 添加到上面的WHEN NOT MATCHED,我们添加到Clears 的额外行以删除1ID 的任何行最终会创建一个额外的行,因为没有任何@目标表中有 987654333@1 行。

【讨论】:

    【解决方案2】:

    一个简单的DELETE-INSERT 不能在这里工作吗?

    DELETE t FROM @targetTable t
    WHERE EXISTS(
        SELECT 1 
        FROM @sourceTable
        WHERE ID = t.ID
    )
    
    INSERT INTO @targetTable(ID, tmstmp, data)
        SELECT ID, tmstmp, data
        FROM @sourceTable s
        WHERE NOT EXISTS(
            SELECT 1
            FROM @targetTable
            WHERE ID = s.ID
        )
    

    您可能希望将这两个语句保留在一个事务中。

    编辑:我刚刚意识到你想要一个单一的声明。但我会把它留在这里作为替代解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-21
      • 2017-05-31
      • 2012-08-09
      • 1970-01-01
      • 2017-05-12
      相关资源
      最近更新 更多