数据库设计:如何处理“归档”问题?


18

我敢肯定,很多应用程序,关键应用程序,银行等等每天都在执行此操作。

所有这些背后的想法是:

  • 所有行都必须有一个历史记录
  • 所有链接必须保持连贯
  • 发出获取“当前”列的请求应该很容易
  • 购买过时物品的客户仍应查看购买的物品,即使该产品不再属于目录

等等。

这是我想要做的,我将解释我面临的问题。

所有的表都有这些列:

  • id
  • id_origin
  • date of creation
  • start date of validity
  • start end of validity

以下是CRUD操作的想法:

  • create =用id_origin= iddate of creation= now,start date of validity= now,end date of validity= null 插入新行(=表示它是当前活动记录)
  • 更新=
    • 读取=读取所有记录,其中end date of validity== null
    • end date of validityend date of validity= now 更新“当前”记录= null
    • 用新值创建一个新值,然后end date of validity= null(=表示它是当前活动记录)
  • delete = end date of validityend date of validity= now 更新“当前”记录= null

所以这是我的问题:与多对多关联。让我们用一个值示例:

  • 表A(id = 1,id_origin = 1,开始=现在,结束=空)
  • 表A_B(开始=现在,结束=空,id_A = 1,id_B = 48)
  • 表B(id = 48,id_origin = 48,开始=现在,结束=空)

现在我要更新表A,记录id = 1

  • 我将记录id = 1标记为end = now
  • 我在表A中插入了一个新值,并且...该死,除非我也重复了该关系,否则我已经失去了关系A_B ...这将终止于表:

  • 表A(id = 1,id_origin = 1,开始=现在,结束=现在+8百万)

  • 表A(id = 2,id_origin = 1,开始=现在+ 8mn,结束=空)
  • 表A_B(开始=现在,结束=空,id_A = 1,id_B = 48)
  • 表A_B(开始=现在,结束=空,id_A = 2,id_B = 48)
  • 表B(id = 48,id_origin = 48,开始=现在,结束=空)

还有……还有另一个问题:关系A_B:我是否应将(id_A = 1,id_B = 48)标记为过时(A-id = 1已过时,但不是B-48)?

怎么处理呢?

我必须进行大规模设计:产品,合作伙伴等。

您对此有何经验?你会怎么做(你做得如何)?

-编辑

我发现这篇文章很有趣,但是不能正确地解决“陈旧过时”(=我实际上要问的问题)


如何在将更新记录的数据更新为具有新ID的新记录之前将其复制,并使用id_hist_prev字段保留历史记录的链接列表。因此,当前记录的ID从来没有得到改变

而是重新发明了轮子,您是否考虑过使用例如Oracle上的Flashback Data Archive
杰克·道格拉斯

Answers:


4

我不清楚这些要求是出于审计目的还是仅仅是简单的历史参考(例如CRM和购物车)。

无论哪种方式,请考虑为每个需要此主区域的主表和main_archive表。“ Main”将仅具有当前/活动条目,而“ main_archive”将具有进入main的所有内容的副本。插入/更新到main_archive可以是插入/更新到main的触发器。然后,针对main_archive的删除操作可能需要更长的时间(如果有的话)。

对于诸如Cust X购买产品Y之类的参考问题,解决您对cust_archive-> product_archive的参考关注的最简单方法是永远不要从product_archive中删除条目。通常,该表的客户流失率应该低得多,因此大小应该不会太在意。

HTH。


2
很好的答案,但是我想补充一点,拥有存档表的另一个好处是它们往往会被非规范化,从而使对此类数据的报告更加高效。还要考虑使用这种方法的应用程序的报告需求。
maple_shaft 2012年

1
在我设计的大多数数据库中,所有“主”表都具有产品名称的前缀,例如LP_,每个重要表均具有等效的LH_,触发器在插入,更新,删除时插入历史行。并非在所有情况下都有效,但它已成为我所做工作的可靠模型。

我同意-如果大多数查询都针对“当前”行,则可以通过将历史数据中的当前分区到两个表中来获得性能优势。为了方便起见,可以将视图合并在一起。这样,具有当前行的数据页就可以在一起,并且可以更好地保留在高速缓存中,并且您不必使用日期逻辑来不断限定对当前数据的查询。
onupdatecascade 2012年

1
@onupdatecascade:请注意(至少在某些RDBMS中),您可以在该UNION视图上放置索引,这使您可以做一些很酷的事情,例如对当前记录和历史记录实施唯一约束。
所有行业的乔恩2012年

5年后,我做了很多事情,而且每时每刻都回想起您的想法。我唯一更改的是在历史记录表上,我有一列“ id”和“ id_ref”。id_ref是对表格实际想法的参考。示例:personperson_h。在person_h我的“ id”和“ id_ref”,其中id_ref有关“ person.id”所以我可以有很多行具有相同person.id(=时的行person被修改),所有id都是我的表都AUTOINC。
奥利维尔·庞斯

2

这与函数式编程有些重叠。特别是不变性的概念。

您有一个叫的表PRODUCT,另一个叫PRODUCTVERSION或类似的表。当您更改产品时,不进行更新,您只需插入新PRODUCTVERSION行。要获取最新版本,您可以按版本号(desc),时间戳记(desc)索引该表,也可以使用标记(LatestVersion)。

现在,如果您有引用某产品的内容,则可以决定它指向的表。它指向PRODUCT实体(总是指该产品)还是指向PRODUCTVERSION实体(仅指该产品的该版本)?

它变得复杂。如果您有产品图片怎么办?它们必须指向版本表,因为可以更改它们,但是在许多情况下,它们不会,并且您不想不必要地复制数据。这意味着您需要一个PICTURE表和PRODUCTVERSIONPICTURE多对多关系。


1

我已经使用所有表上的4个字段实现了这里的所有内容:

  • ID
  • date_creation
  • date_validity_start
  • date_validity_end

每次记录必须进行修改,我复制它,标志着重复记录“老” = date_validity_end=NOW()和当前的一个作为一个好date_validity_start=NOW()date_validity_end=NULL

诀窍是关于多对多和一对多的关系:它可以有效地工作而不会碰到它们!这是关于更复杂的查询的全部内容:要在精确的日期(=现在不是)中查询记录,我需要为每个联接和主表添加这些约束:

WHERE (
  (date_validity_start<=:dateparam AND date_validity_end IS NULL)
  OR
  (date_validity_start<=:dateparam AND date_validity_start>=:dateparam)
)

因此,具有产品和属性(多对多关系):

SELECT p.*,a.*

FROM products p

JOIN products_attributes pa
ON pa.id_product = p.id
AND (
  (pa.date_validity_start<=:dateparam AND pa.date_validity_end IS NULL)
  OR
  (pa.date_validity_start<=:dateparam AND pa.date_validity_start>=:dateparam)
)

JOIN attributes a
ON a.id = pa.id_attribute
AND (
  (a.date_validity_start<=:dateparam AND a.date_validity_end IS NULL)
  OR
  (a.date_validity_start<=:dateparam AND a.date_validity_start>=:dateparam)
)

WHERE (
  (p.date_validity_start<=:dateparam AND p.date_validity_end IS NULL)
  OR
  (p.date_validity_start<=:dateparam AND p.date_validity_start>=:dateparam)
)

0

这个怎么样?对于我过去所做的事情来说,这似乎很简单并且非常有效。在“历史记录”表中,使用其他PK。因此,“ CustomerID”字段是“客户”表中的PK,但是在“历史记录”表中,您的PK是“ NewCustomerID”。“ CustomerID”仅成为另一个只读字段。这样,“历史记录”中的“ CustomerID”将保持不变,并且您的所有关系都保持不变。


很好的主意。我所做的工作非常相似:我复制了记录,并将新记录标记为“过时”,以便当前记录仍然相同。注意我想在每个表上创建一个触发器,但是当您进入该表的触发器时,mysql禁止修改表。PostGRESQL可以做到这一点。SQL服务器执行此操作。Oracle为此。简而言之,MySQL还有很长的路要走,下一次,我在选择数据库服务器时会三思而后行。
奥利维尔·庞斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.