我有一个基本的CRUD网络应用程序,人们可以在其中创建文章/编辑它们。我现在想要添加保留所有编辑的修订历史记录的功能。目前,我有一个看起来像这样的文章表:
Article(id, title, content, author_id, category_id, format)
我考虑了两个用于更改当前架构的选项,以添加对修订历史记录的支持。基本思路是将任何文章的每个编辑都存储为修订表中的记录。所以文章和修订是一对多的关系。
第一个选项(标准化): 一个用于文章元数据的表,一个用于修订。没有存储重复数据。
Article(id, title, category_id)
Revision(id, content, author_id, format)
第二个选项(去标准化): 两个表,如选项1,但有一些重复的列。
Article(id, title, content, author_id, category_id, format)
Revision(id, article_id, content, author_id, format)
我正在考虑使用第二个选项,因为它会使我的编码更容易(更简单,更少的代码行)。我知道它不是“学术性的”和“纯粹的”但我个人的感觉是,不得不做额外的连接会损害代码维护。此外,性能应该更好,因为不必进行很多连接。
这是完成此任务的合理方式吗?可能是我忽略的任何不可预见的或长期的后果?
答案 0 :(得分:7)
如果您关心自己的数据,那么“非规范化”案例中的代码不会少 - 您必须强制Revision
中的最新行始终与Article
中的副本匹配。这在并发环境中实际上是微不足道的 - 你必须非常小心地进行锁定!
(如果您选择Revision
而Article
不包含相同的副本,那么情况会更糟 - 您将无法依赖DBMS来强制执行{{1主键!)
使用足够强大的DBMS,您可以拥有自己的蛋糕并吃掉它 - 例如,Oracle物化视图可以为您“预加入”数据,而无需对实际数据模型进行非规范化。
即使您没有这样的DBMS,只有在测量在实际数据量上的性能之后,才考虑进行非规范化。是的,JOINS可能很贵,但是在你的特殊情况下它们是否太贵了?只有测量才能证明。
Revision
在给定文章下添加修订版时单调增长。
revision_no
PK下面的B树结构使得查找给定文章的最新(或任何!)修订版非常有效。除非您的问题中没有显示备用密钥,否则您还可以cluster Revision
和(在Oracle下)甚至压缩聚类索引的前沿,因此重复Revision
的空间开销被取消。
答案 1 :(得分:5)
性能参数是无意义的 - 您执行的JOIN
更少,但RDBMS针对JOIN
进行了优化。
但是,您可能会从服务器中提取批次更多数据,而这些数据无法进行优化。
您还可能遇到一致性问题。在不同表中复制相同项目的数据会导致出现不一致的情况。如果修订记录和文章记录对format
或author
有不同的值,该怎么办?你怎么知道哪个是正确的?如果content
中的Articles
与任何修订都不匹配,该怎么办?
你真的应该正常化这个。我会在CurrentRevision
表中添加Articles
字段以链接到当前版本,并且ArticleID
表中应该有一个Revisions
来将两者链接在一起。< / p>