在设计数据库时,我经常需要决定是否创建多态表。举个例子,假设我有:
- 一个包含 ID 的折扣表,用于存储折扣码。
- 邮件列表表,注册即可获得折扣
- 参与竞赛即可获得折扣
你可以这样设计:
折扣(id,代码) mailing_list_discounts(mailing_list_id, discount_id) contest_discounts(contest_id, discount_id)
或者你可以去:
折扣(id,代码) auto_discounts(mailing_list_id, contest_id, discount_id)
mailing_list_id和contest_id中总会有一个为空。在正确建立索引的情况下,两者的查询复杂度非常相似,而且我发现通常来说,在较少的表中查找信息比在较多的表中查找信息更容易,因此auto_discounts非常吸引人。
挑战在于,当需要存储与特定类型的折扣相关的信息时,例如contest_winning_attempt ,在这种情况下,对于不适用折扣的情况,我们必须使用null值:
| 折扣ID | 比赛ID | 邮件列表 ID | 赢得比赛的尝试 |
|---|---|---|---|
| 光盘1 | 无效的 | 列表_1 | 无效的 |
| 光盘2 | 比赛_1 | 无效的 | 1 |
| 光盘 3 | 比赛_2 | 无效的 | 2 |
| 光盘4 | 比赛_2 | 无效的 | 3 |
| 光盘 5 | 无效的 | 列表2 | 无效的 |
另一个问题是,当查询单个表时,需要添加空值检查。例如,如果您想找出哪些折扣码是发放给第一次尝试未中奖者的,则需要执行以下操作:
SELECT * FROM auto_discounts 其中 contest_id 不为空且 contest_winning_attempt 不等于 1;
你需要检查contest_id IS NOT NULL ,否则它会选中与比赛无关的记录。你可以添加一个鉴别器,例如type ,其值为contest或mailing_list ,但这会使代码更加冗长且更容易出错。
虽然我有时会因为应用程序中表过多而受到批评,但我通常选择使用单独的表来区分不同的关注点(尽管它们之间有一定的关联)。这样做还有一个好处,就是可以针对每个表优化索引,并且由于不存在被滥用的列,因此误用的可能性也更小。