具有限制的多对多关系的数据库设计
Database design for many-to-many relations with restrictions
我有一个用户数据库和一个问题数据库。我想要的是保证每个用户每个问题只能回答一次
我想到了一个数据库,其中所有问题 ID 作为列,所有用户 ID 作为记录,但是当问题和用户数量增长时,它会变得非常大(而且我猜很慢)。
是否有另一种性能更好的方法?
您可能想要这样的设置。
Questions table (QuestionID Primary Key, QuestionText)
Users table (UserID Primary Key, Username)
Answers table (QuestionID, UserID, Date) -- plus AnswerText/Score/Etc as needed.
在 Answers
table 中,前两列一起形成一个复合主键 (QuestionID, UserID)
并且它们分别是 Question(QuestionID)
和 Users(UserID)
的外键.
复合主键确保QuestionID/UserID
的每个组合只允许一次。如果您想让用户多次回答同一个问题,您可以扩展“复合主键”以包含日期(它将成为一个复合键)。
这是一个规范化的设计,应该足够高效。通常使用 surrogate primary key(如 AnswerID
)代替复合键并使用 unique
约束来确保唯一性——代理键的使用通常是出于易用性的考虑, 但绝对没有必要。
图表
下面是我自己table设计的图,和 by jpw. I made up a few column names to give more of a flavor of the nature of the table. I used Postgres数据类型很相似。
正如该答案的最后一段所讨论的那样,我会在 response_
("Answers") table 上使用一个简单的单一主键,而不是组合主键 fkey_user_
& fkey_question_
.
不切实际
此图符合问题中的问题描述。然而这种设计是不可行的。此场景适用于向用户提出一组问题,永远只有一个调查或测验。在学校、民意调查或焦点小组等现实生活中,我希望我们会向用户提供不止一份问卷。但我会忽略它直接解决问题的措辞。
此外,在某些情况下,我们可能会有问题的版本,因为在连续 quizzes/questionnaires.
给出时,它会随着时间的推移进行调整和修订
性能
您的问题正确地将此问题标识为用户与问题之间的 Many-To-Many 关系,其中每个用户可以回答许多问题,并且每个问题可能由许多用户回答。在关系数据库设计中,只有一种正确的方式来表示 many-to-many。这种方法是添加第三个 child table,有时称为 "bridge table",外键链接到两个 parent table 中的每一个。
在你画 parent tables 比 child tables 垂直向上的图表中,我个人看到这样的 many-to-many作为蝴蝶或鸟类图案的图表,其中 child 桥 table 是 body/thorax,两个 parent 是翅膀。
性能在某种意义上是无关紧要的,因为这是唯一正确的设计。幸运的是,现代关系数据库针对这种情况进行了优化。您应该看到数百万条记录的良好性能。特别是如果您将序列号作为主键值。我倾向于使用 UUID 数据类型;当 table 大小达到数百万时,它们的任意位值可能会降低索引性能(但我不知道细节。
我有一个用户数据库和一个问题数据库。我想要的是保证每个用户每个问题只能回答一次
我想到了一个数据库,其中所有问题 ID 作为列,所有用户 ID 作为记录,但是当问题和用户数量增长时,它会变得非常大(而且我猜很慢)。
是否有另一种性能更好的方法?
您可能想要这样的设置。
Questions table (QuestionID Primary Key, QuestionText)
Users table (UserID Primary Key, Username)
Answers table (QuestionID, UserID, Date) -- plus AnswerText/Score/Etc as needed.
在 Answers
table 中,前两列一起形成一个复合主键 (QuestionID, UserID)
并且它们分别是 Question(QuestionID)
和 Users(UserID)
的外键.
复合主键确保QuestionID/UserID
的每个组合只允许一次。如果您想让用户多次回答同一个问题,您可以扩展“复合主键”以包含日期(它将成为一个复合键)。
这是一个规范化的设计,应该足够高效。通常使用 surrogate primary key(如 AnswerID
)代替复合键并使用 unique
约束来确保唯一性——代理键的使用通常是出于易用性的考虑, 但绝对没有必要。
图表
下面是我自己table设计的图,和
正如该答案的最后一段所讨论的那样,我会在 response_
("Answers") table 上使用一个简单的单一主键,而不是组合主键 fkey_user_
& fkey_question_
.
不切实际
此图符合问题中的问题描述。然而这种设计是不可行的。此场景适用于向用户提出一组问题,永远只有一个调查或测验。在学校、民意调查或焦点小组等现实生活中,我希望我们会向用户提供不止一份问卷。但我会忽略它直接解决问题的措辞。
此外,在某些情况下,我们可能会有问题的版本,因为在连续 quizzes/questionnaires.
给出时,它会随着时间的推移进行调整和修订性能
您的问题正确地将此问题标识为用户与问题之间的 Many-To-Many 关系,其中每个用户可以回答许多问题,并且每个问题可能由许多用户回答。在关系数据库设计中,只有一种正确的方式来表示 many-to-many。这种方法是添加第三个 child table,有时称为 "bridge table",外键链接到两个 parent table 中的每一个。
在你画 parent tables 比 child tables 垂直向上的图表中,我个人看到这样的 many-to-many作为蝴蝶或鸟类图案的图表,其中 child 桥 table 是 body/thorax,两个 parent 是翅膀。
性能在某种意义上是无关紧要的,因为这是唯一正确的设计。幸运的是,现代关系数据库针对这种情况进行了优化。您应该看到数百万条记录的良好性能。特别是如果您将序列号作为主键值。我倾向于使用 UUID 数据类型;当 table 大小达到数百万时,它们的任意位值可能会降低索引性能(但我不知道细节。