为了完整性而去规范化是不是一个好主意?

Pre*_*ids 5 postgresql database-design

我正在使用 Postgres 9.2 开发一个测验应用程序,在该应用程序中,我向用户展示了一系列问题并记录了他们的答案。

这些问题可以有多种形式——它们可能是多项选择(什么是 2 + 2?A:2。B:3。C:4),或者它们可能需要用户自己计算答案,在这种情况下我需要将他们的输入限制为“440”或“1/2”或“.333”之类的内容。一些问题可能会提示用户输入一篇文章。而且,当然,我以后可能需要添加更多类型的问题。

我设想的表格,以简化的形式,是这样的:

CREATE TABLE problems
(
  problem_id serial NOT NULL PRIMARY KEY,
  problem_type text NOT NULL, -- Refers to a lookup table
  answer_letter text, -- If not null, refers to the correct answer in the answers table below.
  response text -- If not null, represents a correct answer to be input, like '0.4'
);

CREATE TABLE answers
(
  problem_id integer, -- Foreign key
  answer_letter text,
  content text,

  CONSTRAINT answers_pkey PRIMARY KEY (problem_id, answer_letter)
)

CREATE TABLE questions
(
  user_id integer,
  created_at timestamptz,
  problem_id integer, -- Foreign key
  answer_letter text,
  response text,

  CONSTRAINT questions_pkey PRIMARY KEY (user_id, created_at)
);
Run Code Online (Sandbox Code Playgroud)

因此,问题表将具有各种约束以确保:

  • 当problem_type 为'multiple_choice'时,answer_letter 不能为null,response 必须为null。
  • 当problem_type 为'user_input' 时,answer_letter 必须为null,response 不能为null。响应也必须仅包含几个字符。
  • 当 question_type 为 'essay' 时, answer_letter 和 response 都必须为空,因为我无法真正为论文问题提供正确答案。

这足够干净,并且可以很好地约束问题表。我可能会使用 enum 而不是 question_type 的查找表,因为无论如何它的所有可能值都已经被烘焙到模式中。

我的困难是,如何约束问题表?它的约束将非常相似(我不希望为引用论文问题的问题提供 answer_letter,等等)。我能想到几个选择:

  1. 创建问题的唯一索引(problem_id、problem_type),向问题添加一个problem_type字段并将其包含在对问题的引用中,然后以类似于问题的方式在检查约束中使用它。这就是我现在倾向于的方式,因为它总体上看起来最干净,但后来我非规范化以实现适当的约束,这对我来说是错误的。
  2. 创建三个问题表,每种类型一个,并分别约束它们。对三个问题表做同样的事情。这对我来说就像纯粹的关系方式,这通常是我想要的,但它也感觉太复杂了。我不想处理联合三个表(或更多,稍后)来获取用户的问题历史记录。
  3. 使用 #2,但使用 Postgres 的继承支持来尽量保持逻辑简单。但是由于您不能将外键指向表的层次结构,因此这不是一个非常干净的解决方案,它是一个需要再次破解的解决方案。
  4. 使用触发器确保问题数据适合相应的问题。也许是因为我对触发器没有太多经验,但我担心将这种命令式逻辑塞进数据库最终会变得难以管理。
  5. 忘记问题限制,在应用程序逻辑中处理它,并希望最好。你不能一直限制一切。当然,我也不太喜欢这个主意。

我觉得我的建模方法存在问题,导致我遇到这些困难,特别是因为我遇到了几个与我模式中其他地方非常相似的情况(这个只是最容易描述的)。也许这只是一个难以建模的域,但我觉得必须有更好的方法,而且我只是没有正确规范化。

帮助?谢谢!

Chr*_*ers 6

您正在了解标准规范化讨论中缺少的内容,即约束依赖关系。一般来说,较宽的表格比较窄的表格提供了更大的可能性。所以我的观点是,你提出的这些问题实际上突出了非规范化的充分理由。我会采用您的第一个解决方案(您现在倾向于使用的解决方案)。

在我看来,好的数据库设计通常会尽可能地规范化,但要确保包含正确数据约束所需的所有列。如果您不介意在父表上添加额外的唯一约束,您可以使用复合外键来完成其中的一些操作。利用数据约束是数据库设计的重要组成部分,不应为了理论上看起来不错的规范化而牺牲它。