为了说明我的意思,想象一下一群学生和一群教授处于某种实习阶段。每位教授都要指导一名学生,而每位学生只能由一位教授指导。因此,我们之间存在着一种强制性的一对一关系。

假设每个学生都有一个学生 ID(主键)、姓名和电话号码,每个教授都有一个教授 ID、姓名和电话号码。在这种情况下,是否有理由使用一个表来列出单个实体的所有属性(即学生 ID、姓名、电话号码、导师 ID 以及姓名和电话号码)?

我的第一个想法是,这种方案破坏了第三种范式,因为教授的姓名和电话号码取决于教授 ID,也许将表分成两部分,让教授表包含教授的姓名和电话号码,并将教授 ID 作为外键放入学生表中是正确的做法。同时,由于这是强制性的一对一关系,这有关系吗?

5

  • 4
    这种特殊的关系目前可能是一对一的,但将来很可能不会。一位教授指导多名学生是很常见的。这种要求很可能在未来发生。不要相信它永远不会改变。此外,还可能存在各种其他意外情况,例如学生指导其他学生。为什么不呢?因此,单独的表和外键听起来是更好的选择。


    – 


  • 1
    明年当同一批教授又要教一批新学生时,您是否也计划使用这个软件?


    – 

  • 2
    您的示例在任何给定时刻可能都是 1:1,但从时间上看是 N:M,即学生可能会更换导师,教授可能会更换学员,尤其是在学生毕业时。重新关联外键是一种比重写每行一半更合理的表示方法。


    – 

  • 我同意@freakish 的主要观点,但“学生指导学生”这个说法有点牵强,需要加以说明。导师的定义是指导他人的人。因此,这个人(也是学生)充当导师。导师/学生是否依赖某种可重复使用的层次结构来重复使用他们的个人信息是一个实施细节,但并不否定这样一种观点,即为了模拟指导行为,“导师”的定义是明确的,无论这个人在其他情况下是否也是学生。


    – 

  • 这意味着每次学生转学或离开学校,你都必须解雇相应的教授。这听起来很荒谬,但我认为这是因为 1:1 的例子并不是真正的 1:1。有多少情况是真正的 1:1?


    – 


11 个回答
11

如果老师和学生之间始终存在严格的 1:1 关系,那么将两者放在同一张表中并不违反第三范式。

从规范化角度来看,单个表中的一对一关系没有问题。这只意味着您在同一个表中有两个唯一 ID,并且必须选择其中一个作为主键。然后您可以为另一个添加唯一约束。

当同一张表中存在一对多关系时,就会出现规范化问题。例如,如果一位教授可以指导多名学生,那么你就不能在同一张表中同时拥有教授和学生,否则数据就会重复。因此,你需要将其分成两个单独的表。

但规范化只是数据库设计的一个方面。同样重要的是数据模型应该切实支持应用程序的要求。虽然我不知道您的要求,但我怀疑严格的 1:1 关系对于现实世界的系统来说是一个过于强硬的假设。

例如,您可能会遇到一位老师退休导致新老师接管学生的情况,或者学生退学导致老师暂时没有学生的情况。另一个问题是您无法保留历史记录:如果教授招收了新学生作为学生,则必须覆盖与之前学生相关的所有数据。

更一般而言,数据库模型始终是现实的简化模型。例如,一个人可能有多个地址和多个电话号码,但在大多数系统中,只记录一个地址和电话号码就完全没问题。您不应该试图真实地模拟“现实世界”。但您应该模拟应用程序正常运行所需的一切。

1

  • 在建立关系模型时,请问 X 是否只能拥有 Y 中的一个。例如,一辆车只有一个车牌。这是严格的 1 对 1 关系的一个例子。导师可以拥有多个学员吗?学员可以拥有多个导师吗?即使只有一个,也可以从更宽松(多个)的关系开始。但如果从严格开始,重构为多个关系将是一项艰巨的任务。因此,请确保它确实是严格的 1:1。


    – 

在关系数据库中建立一对一的强制关系是否有意义?

如果两边都恰好为1(不是零/可选),那么我认为不是。严格的 1-1 关系表明这些应该仅代表单个表条目(在单个表中)

每位教授负责指导一名学生,每位学生也只能由一位教授指导。因此,我们之间是一对一的关系。

但这并不是一对一的关系,因此标题中的问题无效。

你在这里暗示的是,导师只会指导一名学生一次,并且永远不会再指导。除非你确切知道他们将指导谁,否则你也不会将这位导师纳入你的系统。

同时,你对学生也说了同样的话。他们只会被指导一次,之后再也不会被指导,并且在知道他们的导师之前,你不会输入该学生的详细信息。

如果您的断言是正确的(我非常怀疑它们是否正确),那么这又回到了我的最初观点:单独列出导师和学生是没有意义的。您只打算同时创建它们,并且永远不会破坏它们之间的关系。因此,这可以表示为单个条目,将其分散到两个或多个表中没有任何价值。

更有可能的是,你歪曲了你们之间的关系。更有可能的情况是,你有一个导师集合(在他们开始指导之前创建),一个学生集合(在他们被指导之前创建),然后你将生成一个指导课程,而这个课程恰好有一个导师和一个学生。

同时,一个导师可以指导多次,既可以反复指导同一个学生,也可以指导多个学生(但每个课程始终指导一个)。同时,一个学生可以被多次指导,既可以由同一位导师反复指导,也可以由不同的导师指导(但每个课程始终指导一个)。

在给定的时间段内,这种关系可能是排他性的(即今年,导师 A 将指导学生 B,他们都不会同时进行其他课程),但这是业务要求,而不是数据规范化要求。这是两个非常不同的概念。

明年呢?如果出于某种原因,他们的指导突然结束,他们俩都被重新分配怎么办?如果将其建模为 1-1 关系,所有这些边缘情况都变得无法建模。

对您的场景的更务实的解释代表了一种非常不同的关系:

我们可以争论您是否认为会话是学生/导师之间的交叉表,或者您是否认为它是具有两个必需 FK(一个给导师,一个给学生)的独立实体。

就我个人而言,如果它只包含 FK(并且可能包含自己的 PK),我只会称它为交叉表。否则,我称它为独立的实体。但这是主观的。

这使得你的问题的前提无效,因为你实际上并没有处理一对一的关系。

首先,让我把这个问题说清楚:学生和老师这样的概念是人的角色,而不是人的子类——一个人可以同时是学生和老师;与终身人相比,学生和老师是短暂的角色。

接下来,我认为 1:1 或 1:N 关系是对 N:M 关系的优化,因此我从此开始,并且只有当确信它是合适的并且对性能很重要时才执行优化。(当然,我们可以要求 N>=1 并且 M>=1。)

一个人可以有多个电话号码、多个地址、多种联系方式、多个学位、多个导师、多个学员等等。

导师与学徒关系可以被正式化为一个独立的实体,它可以包含有关该关系的附加信息(协议编号、开始日期、主题、首选联系方式、时间表、结束日期等)。

2

  • “我认为 1:1 或 1:N 关系是对 N:M 关系的优化” – 强烈反对。它们是代表不同信息的不同结构。将正确的 N:M 关系转变为 1:1 关系将意味着数据丢失。“优化”意味着性能提升且不会丢失数据。


    – 


  • @JacquesB,是的,在很多情况下,它并不合适,而是一种(可能为时过早的)优化。


    – 


确实,由于存在强制性的一对一关系,因此没有结构上的理由要求使用单独的表。您只需将所有列添加到一个表中,这足以存储数据。

然而,在实践中可能存在拆分表格的其他原因。

一种方法是从概念上组织一个表中本来会有很多列的内容。这并不适用于您给出的示例,但我确实见过一些表中包含太多数据的例子,这些数据在概念上是完全不同的。

另一种方法是以不同的方式控制对表的权限/访问。

第三个方法是控制哪些列在底层物理存储中打包在一起 – 这有时会因访问或锁定模式而对性能产生影响。例如,将列拆分到多个表中,意味着单个行锁现在只锁定部分列,而不同表中的列可以同时且独立地更新。

第四个是重新调整数据库时,无论出于什么原因,直接向现有表添加列被认为过于破坏性,因此需要添加另一个补充表。

我不会说这些情况都很常见,但它确实表明实际数据库工作中可能涉及许多实际、性能、维护和管理问题,这些问题远远超出了设计用于存储数据的基本关系模型的范围。

1

  • 2
    第五种情况是需要更新师生关系。如果使用单独的表,只需交换一些外键,但如果使用单个表,则必须交换许多列。


    – 

在高数据安全至关重要的环境中,这可能很有意义。

例如,在学生/教师关系中,助教可能只能访问学生表(可能只是该表的一个子集),而根本无法访问教师表。这比字段级安全性更容易处理,因为您需要为表中的每个字段单独设置权限,而不是为每个表设置权限。

另一个例子是审计。也许你只想对部分数据进行审计(这可能是一项耗费磁盘空间和 CPU 的操作),而不是对其余数据进行审计(以节省成本)。将需要进入审计日志的数据分离到单独的表中,并与父表建立 1-1 链接,这样就可以实现这一点。

或者您有一个系统,其中某些字段对于检索而言是可选的,但检索起来非常昂贵。想想 BLOB。将它们放入单独的表中,并将该表的外键放入该子表中,可以加快数据检索速度(并减少网络负载)。

范式在老师和学生是一对一关系的情况下,共用一行是没有问题的。但是还有其他表吗?

当您将老师和学生放在同一行时,您就失去了将老师与学生区分开来的能力。

@flater 提出了,即如果老师指导了其他学生,这种情况会随着时间的推移而消失。但是,即使没有这一点,其他表格也可能需要单独引用它们。工资单呢?安全徽章呢?其他东西可能需要为它们各自设置一个唯一的标识符。

不要在真空中进行设计。超越这两个方面,思考一下你做出这一选择会对你的其他需求产生什么影响。

您的例子在任何特定时刻可能是 1:1,但时间上是 N:M,即学生可能会更换导师,教授可能会更换学员,尤其是在学生毕业时。

如果教授和学生的表是分开的,那么这种更改就意味着要重写一些外键。如果教授和学生在同一个表中,那么就意味着要覆盖一半的字段。

此外,这两个实体可能不是孤立存在的。可能有其他实体引用它们。例如,课程可能由教授教授,俱乐部可能由学生领导。这些其他实体可能希望通过主键引用实体,但表只有一个主键。如果教授的 ID 是组合表的主键,那么 Club.leader_id 字段是否引用教授,因此由“斯内普教授的学员”领导?如果导师分配发生变化,您是否必须更新所有俱乐部以引用新导师?

从最纯粹的理论来看,如果您的情况与您所述的完全一致,并且它(或周围的要求)改变的可能性为零:否。

但你可能注意到了,前面的句子里有很多条件句。

也许你正在编制一个数据库,其中包含“很久以前生活的奇数教授,他们各自只教过一名奇数学生(而这些学生又只有一名导师),包括有关这些学生的信息” ……

由于这种可能性很小,我认为 – 虽然从技术意义上讲可能还不算违规– 但当你开始将学生和教授视为个体时(可能已经通过说 studentID 和 professorID 是唯一键来暗示),而不是可怕的不可分割的教授-学生-导师融合时,这种违规就会成为违规。

学生和老师都是人,姓名和电话号码是人的属性。关系有两种一对一关系:人-学生-老师、人-老师。将人添加到模型中可以同时拥有学生和老师。为了彻底起见,可以进一步规范化模型,并用自己的表示来表示电话号码,学生电话号码和教授电话号码之间存在一对一关系,从而导致学生和老师的关系不同…

                - teacher
               /
person-student
               \
                - phone number


person-teacher-phone number

…使每个人可以同时成为学生和老师,并拥有两个电话号码,一个学生电话号码和一个老师电话号码。

1

  • “让每个人能够同时成为学生和老师”——这听起来像是你想要避免的事情?


    – 

“拆分表”的原因之一是性能。jwenting 回答中已经提到了这一点。更紧凑的物理布局,或许更紧凑的索引

特别是,其中一个表可能足够小,可以保存在内存中,从而加快查询速度。

1

  • 如果有两个表,则需要进行连接才能获取相同的信息,这可能比扫描单个表要慢。但无论如何,对于实际规模的学校来说,教授和学生的数量足够少,因此查询性能不是问题。


    – 

不,不是真的。

如果两个表之间强制存在 1:1 关系,则这与向第一个表添加更多列相同。

你可以有一个 1:1/0 关系,其中你想要可选的列。例如

Person
Id, type
1, professor
2, student

Professor
Id, role
1, chemistry

Student
Id, Grade
2, B+