在.NET中创建一个非常标准的在线商店时,我遇到了一些关于我的数据库的架构难题。我有一个表“Orders”,由表“OrderItems”引用。后者引用表“产品”。
现在,订单和订单项表在大多数方面是不可变的,也就是说,无论您何时查看表格,创建的订单及其订单应该看起来都相同(例如,每年打印订单以进行簿记应该得到客户在下订单时得到的相同收据。)
我可以想到实现这种行为的两种方法,其中一种方法今天正在使用:
1.非规范化,其中诸如产品价格之类的值被复制到orderitem表中
2.使引用表不可变。处理产品的代码可以在价格(如价格)发生变化时创建新产品。引用产品的可变表可以更新其引用,而不可变的表可以很好地使用它们的旧引用
您首选的方式是什么?有没有更好,更聪明的方法呢?
答案 0 :(得分:4)
这取决于。我正在编写一个非常复杂的企业软件,其中包括一种文档管理和审计,并在药房中使用。
通常,原始值是非规范化的。例如,如果您在创建订单时只需要客户的当前状态,我会将其存储到订单中。
几乎每个时间点都需要提供更复杂的数据。有两种方法:您创建它们的历史记录,或者实现一个几乎相同的修订控制系统。
历史记录表示存在的每个州都作为单独的记录存储在相同或另一个表中。
我实现了一个修订控制系统,我将记录拆分为两个表,一个用于实际项目,一个用于产品,另一个用于其版本。这样我可以引用整个产品或其任何特定版本,因为它们都有自己的主键。
该系统用于许多实体。我可以安全地从审计跟踪引用修订控制下的对象或其他不可变记录。在一开始,拥有这样一个系统似乎更复杂,但最后它非常直接并且一次解决了许多问题。
答案 1 :(得分:1)
如果价格可能随时间变化,则在Product表和OrderItem表中存储价格不会进行非规范化。规范化规则说每个“事实”应该只在数据库中记录一次。但在这种情况下,仅仅因为这两个数字被称为“价格”并不能使它们成为同一个东西。一个是当前价格,另一个是销售日期的价格。这是非常不同的事情。就像“客户邮政编码”和“商店邮政编码”是完全不同的领域;两者都可以简称为“邮政编码”这一事实并不能使它们成为同一件事。就个人而言,我强烈反对给出具有相同名称的不同数据的字段,因为它会造成混淆。我不会把它们称为“价格”:我会称之为“Current_Price”而另一个称为“Sale_Price”或类似的东西。
在销售时不保留价格显然是错误的。如果我们需要知道这一点 - 我们几乎肯定会这样做 - 而不是我们需要保存它。
每次销售或每次价格变动时复制整个产品记录也是错误的。您几乎肯定会有关于产品的持续数据,例如描述和供应商,每次价格变化时都不会改变。如果您复制产品记录,您将复制所有这些数据,这肯定是非规范化。这会产生许多潜在的问题。比如,如果有人在产品描述中修复了拼写错误,我们现在可能会有新的记录说“4片烤面包机”,而旧记录则说“4片式品尝者”。如果我们生成报告并对描述进行排序,它们将分开并看起来像不同的产品。等
如果关于产品和您关心的唯一数据是价格,那么我只是将价格发布到OrderItem记录中。
如果有大量数据发生变化,那么您希望将Product表分成两个表:一个用于常量数据或其不关心的历史数据,另一个用于需要跟踪数据的数据表历史。比如,有一个ProductBase表,包括描述,供应商,库存号,运输重量等;和一个ProductMutable表,其中包含我们的成本,销售价格以及其他常规更改的内容。您可能还需要一个截止日期,或至少指示哪个是最新的。然后,ProductMutable的主键可以是Product_id加上As_of_date,或者如果您更喜欢所有表的简单顺序键,那么它至少可以引用product_id。 OrderItem表引用ProductMutable,而不是ProductBase。我们通过ProductMutable找到ProductBase。
答案 2 :(得分:0)
我认为非规范化是要走的路。
此外,产品不应该有价格(当它不时变化时,当价格对不同的人意味着不同的价值时 - >零售商,客户,散装卖家等)。
您还可以拥有一个价格历史记录表,其中包含ProductID,FromDate,ToDate,Price,IsActive - 以维护产品的价格历史记录。