关于我能想到的唯一另一种方法是拥有一个基本的消息表,其中包含一个 Id 和一个 TypeId。然后,您的子表(QuoteMessage 和 JobMessage)在 MessageId 和 TypeId 上引用基表 - 但它们上也有 CHECK CONSTRAINTS 以仅强制执行适当的 MessageTypeId。
Table: Message
Fields: Id, MessageTypeId, Text, ...
Primary Key: Id, MessageTypeId
Unique: Id
Table: MessageType
Fields: Id, Name
Values: 1, "Quote" : 2, "Job"
Table: QuoteMessage
Fields: Id, MessageId, MessageTypeId, QuoteId
Constraints: MessageTypeId = 1
References: (MessageId, MessageTypeId) = (Message.Id, Message.MessageTypeId)
QuoteId = Quote.QuoteId
Table: JobMessage
Fields: Id, MessageId, MessageTypeId, JobId
Constraints: MessageTypeId = 2
References: (MessageId, MessageTypeId) = (Message.Id, Message.MessageTypeId)
JobId = Job.QuoteId
与仅 JobMesssage 和 QuoteMessage 表相比,这能为您带来什么?它将消息提升为一等公民,以便您可以从单个表中读取所有消息。作为交换,您从消息到相关报价或工作的查询路径是另外 1 个连接。这取决于您的应用程序流程,这是否是一个很好的权衡。
至于违反 DRY 的 2 个相同的表 - 我不会挂断电话的。在 DB 设计中,它与 DRY 无关,而更多地与规范化有关。如果您正在建模的 2 个事物具有相同的属性(列),但实际上是不同的事物(表) - 那么拥有多个具有相似架构的表是合理的。比把不同的东西混在一起要好得多。