【问题标题】:"Merging" two tables in T-SQL - replacing or preserving duplicate IDs在 T-SQL 中“合并”两个表 - 替换或保留重复的 ID
【发布时间】:2010-10-22 05:13:21
【问题描述】:

我有一个使用相当大的表(数百万行,大约 30 列)的 Web 应用程序。我们称它为 TableA。在这 30 列中,该表有一个名为“id”的主键和一个名为“campaignID”的列。

作为应用程序的一部分,用户可以上传与新“活动”相关的新数据集。

这些数据集的结构与 TableA 相同,但通常只有大约 10,000-20,000 行。

新数据集中的每一行都有一个唯一的“id”,但它们都将共享同一个campaignID。换句话说,用户正在为一个新的“campaign”加载完整的数据,因此所有 10,000 行都具有相同的“campaignID”。

通常,用户正在上传新广告系列的数据,因此表 A 中没有具有相同广告系列 ID 的行。由于“id”对于每个广告系列都是唯一的,因此每行新数据的 id 在 TableA 中都是唯一的。

但是,在极少数情况下,当用户尝试为数据库中已存在的“广告系列”加载一组新行时,要求首先从 TableA 中删除该广告系列的所有旧行,然后再插入新数据集中的新行。

所以,我的存储过程很简单:

  1. 将新数据批量插入临时表 (#tableB)
  2. 删除 TableA 中具有相同活动 ID 的所有现有行
  3. 插入表 A ([columns]) 从 #TableB 中选择 [columns]
  4. 删除#TableB

这工作得很好。

但新要求是在用户上传新数据以处理“重复”时为用户提供 3 个选项 - 用户正在为已在 TableA 中的活动上传数据的实例。

  1. 删除 TableA 中具有相同活动 ID 的所有数据,然后插入 #TableB 中的所有新数据。 (这是旧行为。使用此选项,它们将永远不会重复。)
  2. 如果#TableB 中的一行与TableA 中的行具有相同的ID,则使用#TableB 中的行更新TableA 中的该行(实际上,这是用新数据“替换”旧数据)
  3. 如果#TableB 中的一行与TableA 中的行具有相同的ID,则忽略#TableB 中的该行(本质上,这是保留原始数据,而忽略新数据)。

用户无法逐行选择此项。她选择如何合并数据,并将此逻辑应用于整个数据集。

在我使用 MySQL 开发的类似应用程序中,我使用了“LOAD DATA INFILE”函数,并带有“REPLACE”或“IGNORE”选项。但我不知道如何使用 SQL Server/T-SQL 来做到这一点。

任何解决方案都需要足够高效以处理 TableA 有数百万行,而#TableB(新数据集)可能有 10k-20k 行的事实。

我在 Google 上搜索了类似“Merge”命令(SQL Server 2008 似乎支持该命令),但我只能访问 SQL Server 2005。

在粗略的伪代码中,我需要这样的东西:

如果用户选择选项 1: [我都准备好了 - 我有这个工作]

如果用户选择选项 2(替换):

merge into TableA as Target
using #TableB as Source
    on TableA.id=#TableB.id
when matched then 
    update row in TableA with row from #TableB
when not matched then
    insert row from #TableB into TableA

如果用户选择选项 3(保留):

merge into TableA as Target
using #TableB as Source
    on TableA.id=#TableB.id
when matched then 
    do nothing
when not matched then
    insert row from #TableB into TableA

【问题讨论】:

  • 获取新日期相当容易,您可以在带有左连接的集合中完成更新也应该相当简单,因为您也可以在集合中完成棘手的部分是交易正确,它的complicated enough 正确处理 1 行。

标签: sql tsql merge


【解决方案1】:

这个怎么样?

选项 2:

begin tran;
delete from tablea where exists (select 1 from tableb where tablea.id=tableb.id);
insert into tablea select * from tableb;
commit tran;

选项 3:

begin tran;
delete from tableb where exists (select 1 from tablea where tablea.id=tableb.id);
insert into tablea select * from tableb;
commit tran;

至于性能,只要对tablea(大表)中的id字段进行索引就可以了。

【讨论】:

  • 克里斯,这个解决方案非常有效。但是,我是一个太多的 SQL 新手,无法评估它是否有效。例如,在这个语句中——“从存在的表中删除(从表中选择 1,其中 tablea.id=tableb.id)”——该子选择语句是否对 tableA 中的每一行执行一次?如果 tableA 有 5m 行,这是否意味着该语句执行 5m 子选择?无论如何,非常感谢您提供解决问题的解决方案!
  • 根据我的经验,'where exists' sql 将比 'where x in (select id from tableb)' 更快,这是您的另一个选项。而后一个'in' sql 看起来像它执行一个查询,你认为这很快,对吧?无论如何,我只是试一试,看看需要多长时间。
  • 此解决方案相当昂贵,因为即使没有数据更改,它也可能导致重建一堆索引。在并发使用下,您可能会遇到死锁和主键违规。
【解决方案2】:

当他声称他想要 MERGE 时,您为什么要使用 Upserts? SQL 2008 中的 MAREG 更快、更高效。

我会让合并处理差异。

【讨论】:

    猜你喜欢
    • 2022-01-24
    • 2013-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 2021-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多