我认为这个问题的答案并不完整,因此很重要加入我的观点。原始问题得到了很好的回答,但这里存在一个故障。所以我只考虑下面引用的新增问题:
另一件事是他想在付款表中包括总金额,我认为这是不必要的(只需计算产品的单价和数量)。他指出我们需要该列来计算关键的借方和/或贷方,这对于整个系统管理至关重要,需要用于平衡交易。请告诉我你的想法。
这次编辑很有趣。基于这是一个处理货币的交易系统,它必须是可核算的。我使用了一些基本术语:交易、产品、价格、金额。
在这种情况下,非常普遍甚至是必要的去反范式化。为什么?因为你需要它是可核算的。所以当交易被注册时,就是这样,它可能永远不会被修改。如果你需要进行更正,那么你就进行另一笔交易。
现在你可以计算例如产品价格*数量*税等。这在规范化方面是有意义的。但是然后你将需要完全锁定所有相关记录。所以以产品表为例:如果你在交易发生之前更改价格,那么当交易发生时应该考虑到它。但是如果价格在交易之后更改,则不会影响交易。
因此,仅仅加入transaction.product_id=products.id是不可接受的,因为该产品可能会更改。例如:
2012-01-01 price = 10
2012-01-05 price = 20
Transaction happens here, we sell 10 items so 10 * 20 = 200
2012-01-06 price = 22
现在我们要查询2012年1月10日的交易记录,所以我们需要执行以下操作:
SELECT
transactions.amount * products.price AS totalAmount
FROM transactions
INNER JOIN products on products.id=transactions.product_id
这样会得出10 * 22 = 220,因此是不正确的。
所以你有两个选择:
不允许在产品表上进行更新。因此,你需要将该表版本化,对于每条记录,都要添加一个新的INSERT而不是更新。这样事务就可以指向产品的正确版本。
或者你可以将这些字段添加到事务表中。因此,在插入并保存事务时(在数据库事务中),添加totalAmount到事务表并计算它。
是的,这是非规范化的,但它有一个很好的理由,它使其可以追溯。你只需知道,并且通过交易、锁等进行验证,当发生交易时,它与价格=20等所描述的产品相关联。
除此之外,非规范化仍然有好处,例如很容易运行报告。月份、年份等的总交易金额都很容易计算。
规范化有好处,例如没有重复存储、单一编辑点等。但在这种情况下,你不希望使用该概念,因为这不允许并且不适用于交易日志数据库。
将交易视为现实世界中发生的事情的注册。它发生了,你把它写下来。现在你不能改变历史,它就像当时一样被写下来了。未来不会改变它,它已经发生了。