我们在应用程序中有一个要求,我们需要存储引用以供以后访问。
示例:用户可以一次提交发票,并且该发票包含的所有参考资料(客户地址、计算的金额、产品描述)和计算应该随着时间的推移而存储。
我们需要以某种方式保存引用,但是如果产品名称发生变化怎么办?因此,我们需要以某种方式复制所有内容,以便稍后记录,并且不受将来更改的影响。即使产品被删除,它们也需要稍后在存储发票时进行审查。
这里关于数据库设计的最佳实践是什么?甚至什么是最灵活的方法,例如当用户想要稍后编辑他的发票并从数据库中恢复它时?
谢谢!
我们在应用程序中有一个要求,我们需要存储引用以供以后访问。
示例:用户可以一次提交发票,并且该发票包含的所有参考资料(客户地址、计算的金额、产品描述)和计算应该随着时间的推移而存储。
我们需要以某种方式保存引用,但是如果产品名称发生变化怎么办?因此,我们需要以某种方式复制所有内容,以便稍后记录,并且不受将来更改的影响。即使产品被删除,它们也需要稍后在存储发票时进行审查。
这里关于数据库设计的最佳实践是什么?甚至什么是最灵活的方法,例如当用户想要稍后编辑他的发票并从数据库中恢复它时?
谢谢!
这是一种方法:

本质上,我们从不修改或删除现有数据。我们通过创建一个新版本来“修改”它。我们通过设置 DELETED 标志来“删除”它。
例如:
注意事项:
该模型使用了大量的识别关系。这会导致“胖”外键,并且可能是一个存储问题,因为 MySQL 不支持前沿索引压缩(与 Oracle 不同),但另一方面InnoDB总是将数据聚集在 PK 上,这集群可以有利于性能。此外,JOIN 也不太需要。
具有非识别关系和代理键的等效模型如下所示:

您可以在产品表中添加一列,指示它是否正在销售。然后,当产品被“删除”时,您只需设置标志,使其不再作为新产品可用,但您保留数据以供将来查找。
要处理名称更改,您应该使用 ID 来引用产品,而不是直接使用名称。
我相信您知道,您面临的问题是数据库规范化的结果。解决此问题的方法之一可以从商业智能技术中获取 - 将数据以非规范化状态归档在数据仓库中。
标准化数据:
当查询和存储去规范化时,数据仓库表看起来像
通常,有某种计划的作业可以按计划将数据从规范化数据中提取到数据仓库中,或者如果您的设计允许,可以在订单达到特定状态时完成。(例如已发货)可能会在每次状态更改时存储记录(使用名为 OrderStatus 的字段跟踪当前状态),因此完全非规范化的数据可用于操作/履行过程的每个步骤。将数据归档到仓库的时间和方式将根据您的需要而有所不同。
以上涉及很多开销,但我知道的另一种常见方法会带来更多开销。
另一种方法是将表设为只读。如果客户想要更改他们的地址,您无需编辑他们现有的地址,而是插入一条新记录。
因此,如果我在 Jamnuary 第一次在您的网站上订购时的地址是 AddressId 12,那么我会在 7 月 4 日搬家,我会收到一个与我的帐户绑定的新 AddressId。(说 AddressId 123123,因为您的网站非常成功并吸引了大量客户。)
我在 7 月 4 日之前下的订单会关联到 AddressId 12,而在 7 月 4 日或之后下的订单会有 AddressId 123123。
对需要保留历史数据的每个表重复该模式。
我确实有第三种方法,但搜索它很困难。我只在一个应用程序中使用它,它实际上在这个单一实例中运行良好,它有一些非常具体的业务需求,可以完全按照特定时间点的方式重建数据。除非我有类似的业务需求,否则我不会使用它。
在特定状态下,将数据序列化为 Xml 文档或可用于重建数据的其他文档。这允许您将数据保存为序列化时的数据,保留原始表结构和相关性。
你开启了纯粹主义和实用主义方法之间的永恒争论。
从数据库规范化的角度来看,您“应该”保留所有相关数据。换句话说,假设产品名称更改,保存更改日期,以便您可以及时返回并使用该产品名称以及当天存在的所有其他数据重建您的发票。
“去”规范化的方法是将发票视为“时刻”,在相关表格中记录当天的实际数据。这种方法可以让您在没有任何依赖关系的情况下提取该发票,但您永远无法从头开始重新创建该发票。
当您拥有对时间敏感的数据时,您可以使用 product 和 Customer 表作为查找表,并将信息直接存储在 Orders/orderdetails 表中。
因此订单表可能包含客户姓名和地址,详细信息将包含有关产品的所有相关信息,尤其是价格(您永远不想依赖产品表来获取订单时的初始查找之外的价格信息)。
这不是非规范化,数据会随着时间而变化,但您需要历史值,因此您必须在创建记录时存储它,否则您将失去数据完整性。您不希望您的财务报告突然显示您去年的销售额增加了 30%,因为您有价格更新。那不是你卖的。