【问题标题】:SQL Server - Merge based on all columns / Store historic values of dataSQL Server - 基于所有列合并/存储数据的历史值
【发布时间】:2016-08-01 18:47:40
【问题描述】:

假设我的 SQL Server (2012) 数据库中有下表:

我的表

Col1:      Col2:     Col3:     Col4:    VersionNum:
--------------------------------------------------
Val11      Val21     Val31     Val41    1
Val12      Val22     Val32     Val42    1
Val13      Val23     Val33     Val43    1
                 ...

我有以下数据(比如大约 20000 条记录)想与我当前的表合并:

新数据

Col1:      Col2:     Col3:     Col4:
------------------------------------
Val11      Val21     Val31     Val41
Val12a     Val22     Val32     Val42

因此,第一个值与第一行完全匹配,而第二个值至少有一个元素不同

我希望我的桌子最终看起来像:

我的表

Col1:      Col2:     Col3:     Col4:    VersionNum:
---------------------------------------------------
Val11      Val21     Val31     Val41    1
Val12      Val22     Val32     Val42    1
Val13      Val23     Val33     Val43    1
Val12a     Val22     Val32     Val42    2

或者,用文字来表达:

  1. 如果所有数据元素都匹配,则不要添加新行
  2. 如果任何一个或多个数据元素不同,则使用更新的版本号添加新行。

我发现this question 似乎处理了类似的此类问题,但仅针对主键差异。我想知道的是,鉴于要用于合并的如此大的数据集,最好/最有效的方法是什么?或者,如果有任何更好的模式(存档表或类似的东西,请分享 - 这对我来说都是全新的,我希望尽可能干净高效地做事情)。

【问题讨论】:

  • 我认为你需要澄清这个问题。您的第二个标准说:“如果任何一个或多个数据元素不同,则添加具有更新版本号的新行”,但每个新行都将与几乎每个现有行不同。大概您不想为每个现有行生成 20,000 个新版本?有没有你没有提到的主键?
  • @MikeChristie,这是我的挑战 - 如果任何数据元素不同,他们要求的是连续添加,但不添加任何行是否都匹配......没有主要键。
  • 您希望每个新行的版本号为 2 吗?然后下次你这样做时,所有新行都是第 3 版?
  • 如果新行可以看作是多个现有行的新版本怎么办?您想为每个符合条件的现有行创建一个新版本吗?还是只为最接近的匹配?
  • @MikeChristie,就是这样 - 事实上,我实际上只是给行加上时间戳,但不想进入那个,因为那里有人会评论时间戳而不是我的实际问题。

标签: sql-server tsql merge


【解决方案1】:

一个很棒的方法是使用MERGE()。您可以在MERGE() 中说明如果存在匹配项(更新、插入、删除)等该怎么做,以确保您没有重复项,并且只更新或插入您想要的记录。

https://msdn.microsoft.com/en-us/library/bb510625.aspx

https://technet.microsoft.com/en-us/library/bb522522(v=sql.105).aspx

还有一个很好的例子:

https://www.simple-talk.com/sql/learn-sql-server/the-merge-statement-in-sql-server-2008/

【讨论】:

  • SCSimon,非常感谢 - 我实际上目前正在使用 MERGE 语句来执行此操作,但我必须在每一列上加入它们,看起来这应该是非常低效的。有什么想法吗??
  • @JohnBus​​tos 这是一个可以理解的问题,但这完全取决于您的架构、资源、索引等。您是否注意到冗长的更新?我的环境没有任何问题,但我确实每天都运行作业,这对我来说限制了 SOURCE 表记录很多。
  • 好吧,这太糟糕了,SCSimon,我做了 MERGE 只是发现它有 NULL 值的问题......我现在正在考虑使用这些方法来做这件事:@987654324 @
  • 所有列都为空吗?
  • 完全没有@JohnBus​​tos。事实上,微软假设创建 MERGE 就是考虑到这一点。他们甚至声明“当两个表具有复杂的匹配特征混合时,为 MERGE 语句描述的条件行为最有效。例如,如果它不存在则插入一行,或者如果它匹配则更新该行。当简单地更新一个表基于另一个表的行,可以通过基本的 INSERT、UPDATE 和 DELETE 语句来提高性能和可伸缩性。”因此,它旨在用于使插入或连接语句复杂化时会很慢
【解决方案2】:

我假设您希望所有新行的版本号为 2(或比现有的最大 versionNum 多 1),因为这是唯一有意义的方法。首先使用不带 ALL 关键字的 UNION 获取合并的记录集:

select col1, col2, col3, col4
  from firstTable
union
select col1, col2, col3, col4
  from secondTable

将此称为“query1”。现在加入 firstTable 以标记原始记录:

select a.*,
       f.versionNum
  from (query1) a,
       firstTable f
 where a.col1 = f.col1
   and a.col2 = f.col2
   and a.col3 = f.col3
   and a.col4 = f.col4

这是查询 2。没有 versionNum 的任何东西都是新的,应该有 versionNum = 2。所以:

select b.col1,
       b.col2,
       b.col3,
       b.col4,
       2 as versionNum
  from (query2) b
 where b.versionNum is null

是您要插入的记录。

如果你需要使 versionNum 比表中的当前最大值大一,你可以这样做

(select max(versionNum) from firstTable) + 1

而不是 2 作为 versionNum。

【讨论】:

  • 有趣的方法,Mike,从来没有想过这样做 - 你对 this 和 merge 语句的效率有什么想法吗?
  • 我怀疑,在没有任何证据的情况下,如果您使用合并语句执行此操作,合并可能会更快。就我个人而言,除非我怀疑性能会成为问题,否则我倾向于以这种方式构建事物,因为像这样从内到外构建查询使其更易于阅读。当然,你可能不同意!如果您尝试合并,请告诉我——我会对性能结果感兴趣。
  • 这不包括部分匹配
  • @tomislav_t 你能解释一下为什么吗?任何部分匹配的记录都将无法在联合中获取版本号,因此最终会在数据集中插入到末尾。
  • 在原始示例中,我们 (Val12a,Val22,Val32,Val42) 导致记录的版本 2 (Val12, Val22, Val32, Val42)。如果我们还有一个版本号为 3 的现有记录(Val10、Val22、Val32、Val42),那么新添加的记录的版本号是多少? 4 还是 2?
猜你喜欢
  • 2015-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-01
  • 2020-05-31
  • 2014-12-18
  • 1970-01-01
相关资源
最近更新 更多