在SQL Server中设计1:1和1:m关系

10

在 SQL Server 2008 中,如何设计 1:1 和 1:m 关系?


2
你是指“创建”还是“设计”? - Mitch Wheat
2
@Mitch Wheat:你和你的术语 :p - OMG Ponies
1
@OMG Ponies:也许我表达不太准确 :) 你是指我如何在总体架构中设计这样一个东西,还是指我如何物理创建代表这些关系的结构? - Mitch Wheat
请查看以下关于1:1关系的答案, https://dev59.com/EHI-5IYBdhLWcg3wsKv9 - waheed
3个回答

23
任何关系都需要“父”表(单方面)具有主键(或唯一键),该键唯一标识每行,而“子”表(另一侧)则具有外键列或列,必须使用与父表中某些现有主键值相同的值填充。 如果您想要一对多(1-M)的关系,则外键应该是子表中可以重复的普通属性(列或列),即可以有许多具有相同值的行。
如果您想要一对一(1-1)的关系,则外键本身应该是子表中的主键或唯一索引,它保证子表中最多只能有一行具有该值。
1-1关系有效地将表中的属性(列)分成两个表。 这称为垂直分割。 这经常用于对表实体进行子类化,或者因为在表中的列的使用模式表明其中几列需要更显着地访问比其余列。 (例如,一个或两个列每秒将被访问1000次,其他40个列仅每月访问一次)。以这种方式分区表实际上将为这两个不同的查询优化存储模式。
子类化。 上述实际上创建了一个1到零或一的关系,用于所谓的子类或子类型关系。 当您有两个不同的实体共享许多属性,但其中一个实体具有其他实体不需要的附加属性时,就会发生这种情况。 一个很好的例子可能是员工和薪资员工。 员工表将具有所有员工共享的属性,而薪酬员工表将存在于与员工(1-0 / 1)的关系中,具有仅薪资员工需要的附加属性(工资,年假等)。如果您真的想要一个1-1的关系,那么您需要添加另一种机制来保证子表中始终有一个记录与父表中的每个记录/行相对应。通常唯一的方法是在用于插入数据的代码中强制执行此操作(可以是触发器、存储过程或数据库外部的代码)。这是因为如果您在两个表上添加了要求行始终存在的引用完整性约束,则不可能在不违反其中一个约束的情况下向任何一个表添加行,并且您无法同时向这两个表中添加行。

如果您想要一个一对一(1-1)的关系,那么外键应该是子表中的主键或唯一索引,以确保子表中只有一个具有该值的行。但这实际上不是“一对零或一的关系”,您需要更改措辞为“确保子表中最多只有一行...”。 - onedaywhen
您可以使用触发器在创建父表时强制将记录发送到子表。但是,这样做时,除了在触发器中发送的列(通常只有FK)或具有默认值的列之外,您无法强制执行任何列的非空约束。这在某些情况下可能是好的,但在其他情况下可能是不好的。 - HLGEM

10

一对一关系

Create Table ParentTable
    (
    PrimaryKeyCol ... not null Primary Key
    , ...
    )

Create Table ChildTable
    (
    , ForeignKeyCol ... [not] null [Primary Key, Unique]
    , ...
    , Constraint FK_ChildTable_ParentTable
        Foreign Key ( ForeignKeyCol )
        References ParentTable( PrimaryKeyCol )
    )
在这种情况下,对于给定的ParentTable主键值,我永远不可能拥有超过一个ChildTable中的行。请注意,即使在一对一关系中,其中一个表也是“父”表。在实现上,将一对一关系与一对多关系区分开来的是ChildTable的外键值是否具有唯一或主键约束。
一对多关系
Create Table ParentTable
    (
    PrimaryKeyCol ... not null Primary Key
    , ...
    )

Create Table ChildTable
    (
    , ForeignKeyCol ... [not] null 
    , ...
    , Constraint FK_ChildTable_ParentTable
        Foreign Key ( PrimaryKeyCol )
        References ParentTable( PrimaryKeyCol )
    )
在这种情况下,对于给定的ParentTable主键值,我可以在ChildTable中拥有多行。

1
请注意,第一个示例实际上是1:0..1关系,即没有强制要求在ParentTable中的每一行都有对应的ChildTable行。SQL语言缺乏多重赋值运算符,SQL Server不支持表间CHECK约束或CREATE ASSERTION。今天是否可以在SQL Server中实现真正的1:1关系,而不必诉诸过程性代码(触发器、存储过程等)? - onedaywhen
@onedaywhen - 在MSSQL中,您显然可以在关系的两侧创建FK约束,并将两个FK约束为唯一。但是,在这种情况下输入数据将是一项繁琐的任务。您必须在输入数据时禁用其中一个FK,然后在完成后重新启用它。这意味着任何进行输入的进程都必须具有执行此操作的权限。也就是说,在实践中,真正的1:1(而不是1:0/1)很难实现。 - Thomas
零和一对一关系? - Kiquenet
@Kiquenet - 我不确定我理解你的问题。实际上,1:0/1关系很难实现。它需要一个名为延迟关系的 ANSI SQL 功能,在这个功能中,检查给定的插入/更新是否违反了 FK 关系的操作是在提交时而不是立即进行的。 - Thomas

2
存在一种1:1的关系,即表A和表B在彼此之间只存在一次。例如:一个学生有一条主记录。学生是表A,记录在表B中。表B将包含对表A中学生记录的外键(可能反之亦然)。
存在一种1:m的关系,即表A可以被表B中的许多条目引用或链接。例如:一个学生可以从图书馆借出几本书。学生仍然是表A,而书可以是表B中的条目。表B中的条目将包含谁借了这本书的外键,并且许多书可能引用相同的学生。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接