问题
考虑以下数据库表:
- 产品
- 订购
- 订单详细信息
- 用户
产品具有以下列:
Product_Name,Product_Description,Product_Size,Product_Cost,Product_Unit
订单包含以下列:
Order_number,Order_Total,Order_Status,Order_Payment_Status,Order_UserId (Fk of user table),Order_date
“订单详细信息”具有以下列:
OrderDetails_OrderId(Fk of Order table),OrderDetails_ProductId (Fk of Product table),OrderDetails_Quantity
用户具有列:
User_Name,User_Phone (unique),User_Email (unique),User_Address
考虑,要下达,打包,交付,取消,关闭的订单状态。
现在,用户u1有3个订单:
- 订单O1->放置状态(可由用户编辑)
- 订购O2->放置状态(可由用户编辑)
- 订单O3->已关闭状态(用户不可编辑,但可通过管理员编辑)
现在的情况是用户u1更新他的信息。更新后的信息应仅在O1和O2中开始反映,因为它们仍处于放置状态;而O3已经为用户关闭,现在仅开放给管理员编辑-因此O3仍应反映以前存在的旧用户信息。使用当前的数据库结构-这是不可能的。
类似地,如果管理员以关闭的顺序编辑那里的产品,则不应以关闭的顺序显示编辑内容。
您可能已经发现,上面描述的当前结构是一个简单的与外键相关的结构,其中一个编辑显然会直接反映所有相关实体。
我想出了什么解决方案?
解决方案1:版本化 切勿更新任何行/条目。始终为任何更改添加新行(软更新)。继续添加带有一些标记/标识/时间戳/审核跟踪(已编辑)的行,并使用映射表将版本与订单表进行映射。
即
User_Name | User_Phone | User_Email | User_Address | Version/Timestamp
abc | 123 | abc@email.com |someaddres | v1
abc | 234 | abc@email.com |someaddress | v2
new mapping table
version | order_id
v1 | o3
此解决方案的缺点:
- 同一实体的同一表中有多个条目-那么我们将无法使用唯一键。在这里,电话和电子邮件是唯一的,但是如果我们采用这种方法,则必须删除唯一的索引。
- 所有具有User表外键的表(与顺序无关)都会产生影响。例如,user_feedback表仅具有用户的外键,但是现在由于有多个条目具有同一用户的不同版本,因此该表将受到不必要的影响。
- 当用户数量增加时,某些查询的性能将会受到影响。
- 用户的电子邮件是用于登录的身份。在同一张表中无论如何都无法进行复制。
否,这不是审核跟踪! 根据我们的要求,我们要保留给o3的旧信息仍应保持可编辑状态。因此,还必须对这些编辑进行审核。因此,审核跟踪将完全是一个单独的包装器。
解决方案2:订单关闭后,创建一个新表,其中包含保存所有各个表的json / dump的列 即
new table
order_id | JsonOfUser | JsonOfProductDetails | ...
o3 | {"name":"abc",...} | ... |
此解决方案的缺点:
- 转储的内容是可编辑的,但是这里转储的数据很难编辑,因为现在表已更改,并且此表具有有效地要进行编辑的string / jsonb列,并且其他导航也已删除(非规范化),因此所有可能由于编辑而发生的计算更改也必须手动完成。
- 此表中审核的审核跟踪会很麻烦,因为我们将在此处审核json编辑。
- 深层子jsons-增加代码复杂度。
解决方案3:创建结构完整的所有表的副本,这些副本与根据状态事件的顺序相关联 即
User_Common
User_Closed
对于订单O3,在关闭时,user_common的所有详细信息将被复制到User_closed,具有User_common表的外键的订单O3将被User_Closed表的外键更改。现在,o3中的任何更改都将有效地覆盖旧数据,所有其他未结/下达的订单仍可以从User_common获取更新的信息。
此解决方案的缺点:
- 假设符合此要求的订单中恰好有10个这样的表,则必须制作每个表的副本
- 基于订单的事件/状态,每个实体现在都由两个表有效地表示-可能发生同步问题和数据保存问题-即可维护性。
- 此处订购表的外键正在更改。如此有效地在订单表中,将有两个外键列:一列用于user_common,另一列用于user_closed。因此,当订单打开时,user_closed外键将保持为空,而当订单关闭时,它将被填充。在此之前,仍然会发生1种数据操作,一种是将有关订单关闭的信息从user_common表复制到user_closed。
- 在代码中,我们总是必须根据订单状态(另一个DB调用)来使数据库检查是否应该在公用表或封闭表中进行查找-导致代码级的认知复杂性
这是对我们的要求和研究中提出的解决方案的最小伪造。在不增加不必要的复杂性的情况下,可以满足此要求的实际可行设计是什么?