以下哪种数据库设计更适合内部消息系统。
三张桌子:
MessageThread(models.Model):
- subject
- timestamp
- creator
Message(models.Model):
- thread (pk)
- content
- timestamp
- sender
MessageRecipient
- message_id (pk)
- recipient (pk)
- status (read, unread, deleted)
两张桌子:
Message
- thread_id
- subject
- content
- timestamp
- sender (fk)
MessageRecipient
- message_id (fk)
- recipient (fk)
- status (read, unread, deleted)
其中一种相对于另一种的优势是什么?
第一个模式遵循更好的规范化规则,因此在大多数情况下可能更好。
拥有一个
thread_id
(基本上是一个自然键),而不是另一个表的 FK 可能是自找麻烦。当你想要它是唯一的时候,要强制它是唯一的,当你想要它是相同的时候,要强制它是相同的,这将是非常困难的。出于这个原因,我会鼓励第一个建议的模式。
您的第二个模式允许更改线程中每条消息的主题。如果这是您想要的功能,则不能使用第一个选项,因为您已经编写了它(但请参见下文)。
Message
- id
- parent (fk to Message.id)
- subject
- content
- timestamp
- sender (fk)
MessageRecipient
- message_id (fk)
- recipient (fk)
- status (read, unread, deleted)
您可以拥有
thread_id
概念,而不是 parent
概念。那么每条回复都会指向原始消息的记录。这允许线程化,而无需“线程”表。这样做的另一个可能的优点是它也允许线程树。简而言之,您可以通过这种方式表示消息和回复之间更复杂的关系。如果您不关心这一点,那么这不会对您的申请带来好处。
如果您不关心我刚才提到的线程优势,我可能会推荐您的两种模式的混合:
MessageThread(models.Model):
- id
Message(models.Model):
- thread (pk)
- subject
- content
- timestamp
- sender
MessageRecipient
- message_id (pk)
- recipient (pk)
- status (read, unread, deleted)
这与第一个模式类似,只是我将“主题”列从
MessageThread
移至 Message
表,以允许主题随着线程的进展而更改...我只是使用 MessageThread 表充当 Message 中使用的线程 ID 的约束(这克服了我在答案开头提到的限制)。您可能还想将其他元数据包含在 MessageThread 表中,但我会将其留给您和您的应用程序。
如果稍后您想要添加一些额外的线程属性,例如“锁定”、“粘性”或“重要”,则单独的
MesageThread
表会很有用。不过,仅仅为了将来可能添加额外的功能而选择更复杂的模型通常不是一个好主意。
第一个模型(带有 MessageThread 表的模型)保证线程中的所有消息具有相同的主题,在第二个模型中,线程中的每个消息可以具有不同的主题。这可能是好事也可能是坏事,具体取决于您希望消息传递如何发挥作用。
第一个模型可以将
message.thread_id
列声明为外键,因此您无法在没有有效线程引用的情况下插入消息。对于第二种模型,您没有这样的保证。这可能会在以后引起一些错误。
我不认为第一个模型中的
MessageThread.timestamp
和 MessageThread.creator
列是真正需要的;这些不是与线程中第一条消息的时间戳和创建者相同吗?这种冗余可能会产生负面后果。
我会选择第一个模型,但我会从
MessageThread
中删除创建者和时间戳字段。