关系数据库:当前数据与历史数据,最佳实践
Posted
技术标签:
【中文标题】关系数据库:当前数据与历史数据,最佳实践【英文标题】:Relational Databases: Current Data vs. Historical Data, best Practice 【发布时间】:2018-04-21 03:16:32 【问题描述】:让我们以关系数据库为例,例如mysql。为简单起见,我将专注于重要的事情:拥有一个包含订单的表,其中包含 order_id
(主键)order_date
之类的字段和一个外键 fk_supplier,它引用表中的主键 supplier
.该表还有一个名为supplier_name
的字段。
现在,让我们想象一下,有一个 php-website 显示了在表格中进行的所有订单。表的每一行都由order_id
、order_date
和supplier_name 组成(sql 语句连接了两个表)。
到目前为止一切都很好。现在,有人更改了其中一个订单中引用的一个供应商的名称:历史数据变为不真实或虚假。
我的问题是:防止这种情况的最佳做法是什么?我想到了三个解决方案:
-
不要让用户更改订单中引用的供应商数据行。如果名称发生变化,让他添加新的供应商。
始终将当前供应商数据(例如供应商名称)与订单记录一起保存,不要使用主键/外键引用。
引入时间片:每次,供应商的一个重要属性(如名称)发生变化,创建一个新的时间片。不仅参考订单中的supplier_id,还参考对应的时间片。
所有这些方法都有优点和缺点。例如,第 2 点似乎很脏,并且违反了关系数据库的所有规则。在我看来,第 3 点通常是要走的路。但是需要很多努力,编程明智。用户体验/可用性也变得非常糟糕。
我想听听经验丰富的开发人员和数据库设计人员如何处理这个问题。
【问题讨论】:
我将作为选项 2 实施,但我看不到(从您的描述)为什么您需要以旧订单存储过时的(以前的)供应商名称? IMO 供应商表应该有SupplierID INT
这是主键和名称是无关紧要的。
我会考虑选项 3 的变体,您只需创建一个新的供应商并添加一个额外的列来存储 OriginalSupplierID
,而不是时间片,以便能够跟踪关系。可能有更好的解决方案,但需要更多背景信息。
在这种情况下,我的选择是 2 但您仍然将 PK 和 FK 保留到供应商记录,同时将供应商详细信息的快照保存在另一个表中,例如OrderInvoiceDetails。
至于"Point 2 e.g., seems pretty dirty and against all rules of a relational database"
:不,不是。您存储完整的订单,以便重新打印。它独立于当前的供应商名称和地址,因此您将数据与订单一起存储。这是一个有效的选项,不违反数据库规范化。
@whocares81:我的意思是:这不是多余的。您存储带有名称和地址的订单。同一供应商的另一个订单可以有另一个名称和地址。同时,供应商可以拥有另一个名称和地址。这有什么多余的?这是存储数据的正确方法。顺便说一下,订单中的商品价格也是如此。但是,正如您已经展示的那样,还有其他选择。我要说的是:这不违反规范化并且是一个有效的选择。
【参考方案1】:
选项 3 的一种形式,其中您有关于供应商信息的 StartDate 和 EndDate。这样,数据在所有时间都是准确的(供应商名称在给定时间是正确的)。您还可以做的一件事是创建一个电子表格,其内容每晚都会加载到包含供应商信息的数据库中(进入 fact_Supplier 表或查找表)。对供应商的所有编辑都通过此电子表格进行,只有那些将负责此类事情的选定人员才能访问。如果电子表格中有更改,则供应商表中的先前信息将结束日期,并插入带有新信息的新记录。任何更改都会发生这种情况,供应商名称、供应商地址等。
【讨论】:
【参考方案2】:将扩展我的评论:
我会选择选项 2,但有一些修改:
供应商表应保持原样。 订单表应继续引用供应商表 创建新表,例如OrderInvoiceDetails 应与 Order 表具有 1 对 1 的关系,与供应商具有 FK。此表将包含供应商详细信息的快照。优点:
简单高效的查询 如果需要,可以从供应商表中单独修改发票详细信息。 我认为从逻辑上讲这是最佳解决方案,因为您希望存储与特定订单相关的供应商详细信息,而不是存储供应商历史数据。 旧数据可以与订单数据一起轻松存档缺点:
存储冗余数据,尤其是对于详细信息不经常更改的供应商【讨论】:
以上是关于关系数据库:当前数据与历史数据,最佳实践的主要内容,如果未能解决你的问题,请参考以下文章
数据库设计模型实体关系图n实体与1:m实体的关系 - 最佳实践
为多个数据库关系配置 Hibernate-Spring 的最佳实践是啥?