SQL服务器数据备份/仓库

时间:2014-05-21 00:02:12

标签: sql sql-server-2008

我被要求对数据库中的某些表格进行快照,因此将来我们可以清楚地了解过去任何一天的情况。让我们说其中一个表看起来像这样:

GKEY   Time_in          Time_out           Category          Commodity
1001   2014-05-01 10:50  NULL              EXPORT            Apples
1002   2014-05-02 11:23  2014-05-20 12:05  IMPORT            Bananas
1003   2014-05-05 11:23  NULL              STORAGE           Null

执行快照的简单方法是使用另一列SNAPSHOT_TAKEN(Datetime)创建表的副本,并使用INSERT语句填充它

INSERT INTO UNITS_snapshot (SNAPSHOT_TAKEN, GKEY,Time_in, Time_out, Category, Commodity)
SELECT getdate() as SNAPSHOT_TAKEN, * FROM UNITS

好吧,它工作正常,但它很快就会使目标表变大,特别是如果我想经常运行这个查询。更好的解决方案是检查当前活动表和最新快照之间的变化并将其写下来,省略所有尚未更改的内容。 有没有简单的方法来编写这样的查询?

编辑:"前进三角洲"的可能解决方案(假设没有从原始表中删除)

INSERT INTO UNITS_snapshot 
SELECT getdate() as SNAP_DATE,
r.*   -- Here goes all data from from the original table
CASE when b.gkey is null then 'I' else 'U' END AS change_type

FROM UNITS r left outer join UNITS_snapshot b
WHERE (r.time_in <>b.time_in or r.time_out<>b.time_out or r.category<>b.category or r.commodity<>b.commodity or b.gkey is null)
and (b.snap_date =
(SELECT max (b.snap_date) from UNITS_snapshot b right outer join UNITS r
on r.gkey=b.gkey) or b.snap_date is null)

假设:不删除原始表中的值。也许WHERE中的每个字段都应该是COALESCE(xxx,&#39;&#39;),以避免将空值与设置值进行比较。

1 个答案:

答案 0 :(得分:2)

Dan Bracuk和ITroubs都发表了非常好的评论。

解决方案1 ​​ - 每日快餐店

您提出的第一个解决方案非常简单。您可以使用简单的查询构建快照,您也可以查询它并使用非常简单的查询重建任何一天的快照,只需过滤SNAPSHOT_TAKEN列。

如果您只有几千条记录,我会选择这一条记录,而不必过多担心它的增长规模。

解决方案2 - 具有滚动历史记录的每日快餐店

这与解决方案1基本相同,但随着时间的推移,您只保留一些快照...以避免快照数据库随着时间的推移而无限增长。

最简单的方法是保存最近N天的快照......可能是一个月或两个数据。更复杂的方法是保持快照的密度取决于年龄...例如,您可以拥有上个月的每一天,加上过去3个月的每个星期日,加上每个月末的去年等等...

此解决方案要求您开发一个过程来处理删除不再需要的快照。它不像在查询中使用getdate()那么简单。但是你在空间和历史信息之间取得了很好的平衡。您只需要平衡良好的快照保留策略以满足您的需求。

解决方案3 - 前向行增量

构建任何类型的delta都是一个复杂得多的过程。

通过存储初始快照(就好像当天已插入所有行)构建转发增量,然后在以下快照中,仅存储有关快照(N)和快照(N-1)之间差异的信息)。这是通过分析每一行并仅在行是新的或更新或删除时存储数据来完成的。如果主表的长度变化不大,则可以节省相当多的空间,因为没有信息存储在未更改的行中。

显然,要处理增量,你现在需要2个额外的列,而不仅仅是一个:

  • delta id (如果您只需要1个delta,那么snapshot_taken很好 天)
  • 行更改类型(可以是D =删除,I =已插入,U =已更新...或 类似的东西)

主要的复杂性源于必须识别行(通常通过主键),以便计算两个快照之间是否已插入,更新,删除任何单独的行......或者不是上述任何一行。

另一个复杂性来自于读取快照数据库和构建最新(或任何其他)快照。这是必要的,因为在表中只有行差异,您不能通过过滤snapshot_taken来简单地选择一天的快照。

这在SQL中并不容易。对于每一行,您必须考虑最终版本...具有MAX snapshot_taken的那个&lt; =您要构建的快照的日期。如果是插入或更新,则保留该行的数据,否则(如果是删除)则忽略它。

要构建快照增量(N),必须首先从快照数据库构建最新快照(N-1)。然后,您必须按主键或行标识比较两个快照,并计算更改类型(I / U / D)并在快照数据库中插入更改。

请注意,如果不首先合并旧快照数据,则无法删除旧快照数据。这是因为所有快照都是从最早的初始数据和所有后续差异数据计算出来的。如果您要删除一年的旧快照,则必须在新的初始快照中合并旧的初始快照和所有年份的变体。

解决方案4 - 向后行增量

这与解决方案3非常相似,但有点复杂。

通过存储最终快照构建后向增量,然后在以下快照中,仅存储有关快照(N-1)和快照(N)之间差异的信息。

优点是通过快照数据库上的简单选择,可以随时获得最新的快照。您只需要在检索旧快照时合并差异数据。将此与前向增量进行比较,您总是需要从差异数据重建快照,除非您真正对第一个快照感兴趣。

另一个优势(与解决方案3相比)是您可以通过删除早于特定快照的差异数据来删除旧快照。您可以轻松地执行此操作,因为快照是根据最终快照而非初始快照计算的。

缺点在于模糊的逻辑。差异数据向后计算。值必须存储在(U)pdate和(D)elete变量上,但在I变量上是不必要的。如果找到的第一个变体是(I)插入,则必须忽略行。可行,但有点棘手。

解决方案5 - 前向和后向列增量

如果主表有许多列,或许多长文本或varchar列,并且只更新了一堆,那么只存储列变体而不是行变化是有意义的。

这是通过使用具有此结构的表来完成的:

  • delta id (如果您只需要1个delta,那么snapshot_taken很好 天)
  • 更改类型(可能是D =删除,I =已插入,U =已更新...或 类似的东西)
  • 列名称

根据行增量,可以向前或向后计算差异。

我已经看到了这一点,但我真的建议不要这样做。缺点太多,增加了复杂性。

值是一个text或varchar,如果你有数字,布尔值或日期/时间值,还有要处理的类型转换问题...而且,如果你有很多这些问题,你很可能会赢得#39你可以节省尽可能多的空间。

重建任何快照都是地狱。总而言...... 对此类表格的任何操作确实需要对主表格结构有很多了解。