数据库设计:Q& A的灵活存储

时间:2011-05-17 00:58:35

标签: database-design relational-database

我有一段时间没有设计数据库,我现在对我的设计没有太多信心。我基本上在数据库中有三个表,表示各种历史记录。我必须改变这个系统,以便每次添加记录(例如某些东西变成历史记录)时,可能需要来自用户的一些输入。有时他们只会有一个简单的问题,有时他们会被要求提供五种不同的信息,而这些信息需要由最终用户通过管理前端灵活管理。所以我计划有一个Question表和Answer表,用复合表将这些表与其他三个表联系起来。我正在努力设计答案表,因为每个问题都需要各种各样的答案。一些响应将是通过外键绑定到另一个数据库的下拉选择。其他可以是文本输入,日期或是/否答案。现在,我有一个答案表,其中包含所有类型的答案以及一系列可以为空的字段......

+----------------------------+
| Answer                     |
+----------------------------+
| Id (int)                   |
| QuestionId (int)           |
| ForeignKeyId1* (int)       |
| ForeignKeyId2* (int)       |
| ForeignKeyId3* (int)       |
| Number* (bigint)           |
| DateField* (date)          |
| Text* (varchar 500)        |
| YesNo* (bit)               |
+----------------------------+
  *Nullable

附注:问题表与QuestionType相关联,该类型将指示(在应用程序内)如何验证用户输入。输入存储在Answer记录中的适当可空字段中,其他字段为空。我认为这比捕获所有varchar应答字段没有数据完整性更好。

这是一个糟糕的设计吗?什么能让它变得更好?

2 个答案:

答案 0 :(得分:1)

根据您的描述,您的解决方案看起来应该可以正常工作。我已经对表格进行了类似的设置,这些表格设置了列以记录特定类型的数据,允许其他字段可以为空。看起来不错,虽然我不确定您的ForeignKeyID字段是什么:\

答案 1 :(得分:1)

对于不同的数据类型具有不同的字段是可以的,因为具有多个外键。此设计将尽可能多的数据域完整性推送到DBMS中。这在哲学上是一个很好的方法,因为大多数人会告诉你让DMBS为你工作将节省你编写和维护代码。我大部分时间都会成为那些人之一。

从可维护性方面来看,还有另一种方法可以解决这个问题。如果您添加新类型的数据类型(例如浮点数或GUID),您将不得不返回并修改ANSWER表的结构。类似地,如果您创建另一个需要新查找表答案的问题类型,则必须返回并添加另一个FK字段。

你可以改变你的ANSWER表来使用catch-all方法,所以看起来更像是这样:

ANSWER
( Id (int)
, QuestionId (int)
, Part (int)
, Value (nvarchar 1000)
)

如果我正确地理解了你的问题,那么一个问题可能有多部分的答案。假设单个问题可能存在多个答案,并且您的问题控制表知道哪个部分是哪个,您可以使用“部件”字段来区分这些部件。如果每个问题只有零个或一个答案,那么您不需要答案表,只需添加答案即可。

那么,为什么你想放弃让数据库强制执行数据域完整性呢?以下是相对的利弊:

多个单独答案字段的优点:

  • DBMS不允许您将错误类型的数据放入字段中,因此在存储数据之前不需要编写或调用任何数据域完整性函数(除非您想在GUI级别捕获这些错误) )
  • 关于如何解释答案的内容(例如,哪个月是哪一天,以及该日期答案中的哪一天......),并没有混淆......)

抓住所有答案字段的优点:

  • 您不必编写额外的代码 看看问题类型找出来 ANSWER表中的哪一列 阅读任何特定类型的问题。
  • 你不需要改变你的 数据库模式/物理数据库 每次新的查找时都有人口 表格或答案数据类型已添加。

在任何一种情况下,您都必须编写一些代码来处理这样一个事实:您在一个数据存储中存储了多个问题的语义不同的答案。您必须决定要编写哪种类型的额外代码,确定要放置和找到每种类型答案的类型,或者确定如何使用公共表示来存储和解释不同数据类型的类型(即字符串) )。

鉴于修改一个包含数据的表是一件痛苦的事情,并且考虑到大多数编程语言都内置了非常强大的.ToString()/ .TryParse()类型功能,我倾向于使用全能方法如果我主要担心的是可维护性。