关系数据库设计(规范化多对多映射)

问题描述 投票:0回答:5

以下是我面临的设计问题的类似(和简化)示例:

假设您有学生、班级和成绩。学生可以在许多不同的班级。每个班级都有许多不同的学生。每对(学生、班级)都有一个成绩。

我应该像这样布局数据库(mysql 数据库)吗:

选项 1)

students table - (student_id, student_name)
classes table - (class_id, class_name)
students_classes table - (student_class_id, student_id, class_id)
grades table - (student_class_id, grade)

选项2)

students table - (student_id, student_name)
classes table - (class_id, class_name)
grades table - (student_id, class_id, grade)

还是应该设计成别的东西?

选项 2 现在看起来更简单,但将来,我可能需要与每个 (student_id,class_id) 对相关的其他统计信息,在这种情况下,选项 1 可能更好。不过选项 1 过于复杂。

sql relational-database database-normalization
5个回答
3
投票

选项3)

students table - (student_id, student_name)
classes table - (class_id, class_name)
students_classes table - (student_class_id, student_id, class_id, grade)

成绩是学生班级的一个属性。

除非Grade有成为正式实体的可能性。在这种情况下:

选项 4)

students table - (student_id, student_name)
classes table - (class_id, class_name)
students_classes table - (student_class_id, student_id, class_id)
grades table - (grade_id, grade, student_class_id)

3
投票

我个人会选择选项2。成绩的复合主键没有任何问题,它捕获数据模型中所需的信息。

在选项 1 中,students_classes 除了具有代理键之外没有其他任何用途。

看到其他答案后进行编辑:

  • 2NF:成绩(非关键)仅取决于学生/班级(关键)
  • 3NF:不适用。你对非关键依赖没有非关键
  • BCNF:不适用,你只有一个候选键

1
投票

选项 2 是正确的,但它应该被称为

student_class
,反映其 n::n 函数,或 Enrollment 作为实体。
(student_id, class_id)
是PK。

Grade(如您所示)是对该复合键(而不是一个或另一个元素)的 1::1 依赖,而不是其他元素,因此它是

student_class
的一个属性。

因此

student_class
属于3NF。

如果人们没有像选项 1 那样盲目地在所有移动的东西上粘贴

Id
列,他们将能够更好地理解数据,从而更好地标准化。 (以选项 1 中的
Id
列作为起点)干扰了您的直觉,即
(student_id, class_id)
是标识符;不需要额外的
Id
列及其附加索引。然后当你开始评估
grade
时,它对 PK 的依赖性是显而易见的。

Id
列会损害数据库的关系能力。如果您在层次结构中有三个表,并且需要从顶部和底部的表中获取一些列,则您将被迫遍历中间的表。如果您有关系标识符,而不是 Id 列,则您可以从底部表到达顶部表,而必须读取中间表。

“规范化”数据库中有如此多的连接,这一说法只对了一半。完整的事实是,由于数据库没有正确规范化,是的,您被迫进行比必要的更多的连接。在真正规范化的数据库中,使用相同的表,代码需要的连接要少得多。

这是来自最近作业的大学数据模型,简化版本。

IDEF1X 符号,供需要符号解释的人使用。

  • 请注意,仅需要一个代理密钥。

  • 这是因为在替代方案中,(LastName+FirstName+Initials_BirthDate+BithDate) 将是 Person PK,并且它将作为 FK 在 5 个子/孙表中携带,即 81 字节,这是不明智的。

  • 看看你是否能体会到标识符(实线)被传递给子孙;他们有,并且传达意义

  • 当我们有一个完美的 PersonId(即外键并且已经是唯一的)时,为 TeacherId、StudentId、StaffId 添加代理键是愚蠢的。 (这些列的命名是为了标识它们的角色。)

  • 所有业务规则均在DDL中实现:FK Constraints;检查约束;规则。

  • 房间有4列复合钥匙;既定课程具有 3 列复合密钥;两者一起消除重复预订。

  • 既定课程 PK 和学生 PK 共同构成注册 PK(与此问题相同;PK 由不同的列组成,仅此而已)。


0
投票

我是第三范式的粉丝,其中有单独的学生、班级和成绩表,并将它们与多对多表(如班级学生和成绩班级)链接。

但这取决于你以后想如何维护它。最终归结为未来的扩展和可维护性。这就是为什么我更喜欢 3NF。

编辑

Axn 的答案比我的好得多。


-1
投票

这一切都取决于情况,真的。选项 1 可能是执行此应用程序的最可靠的方法;对于本次迭代,选项 2 可能会让您更快地到达那里。从选项 2 -> 1 的改变将来会那么痛苦吗?您有多确定自己需要额外的灵活性?

我建议只选择选项 1。查询不会那么复杂,如果您使用 ORM(例如 ActiveRecord for Rails 等),那么差异几乎为零。

© www.soinside.com 2019 - 2024. All rights reserved.