主键和外键是两种类型的约束,可用于强制 SQL Server 表中的数据完整性。 这些是重要的数据库对象。
本主题包含以下各节:
主键约束
表通常具有包含唯一标识表中每一行的值的一列或一组列。 这样的一列或多列称为表的主键 (PK),用于强制表的实体完整性。 由于主键约束保证唯一数据,因此它们经常在标识列上定义。
如果为表指定了主键约束,数据库引擎将通过为主键列自动创建唯一索引来强制数据的唯一性。 当在查询中使用主键时,此索引还允许对数据进行快速访问。 如果对多个列定义了主键约束,则值可能在一列内重复,但主键约束定义中所有列中的值的每个组合都必须是唯一的。
如下图所示,购买.ProductVendor 表中的 ProductID 和 VendorID 列构成了此表的复合主键约束。 这可确保 ProductVendor 表中的每一行都具有 ProductID 和 VendorID 的唯一组合。 这样可以防止插入重复的行。
一个表只能包含一个主键约束。
主键不能超过 16 列,总键长度为 900 字节。
主键约束生成的索引不能导致表上的索引数超过 999 个非聚集索引和 1 个聚集索引。
如果主键约束未指定为聚集或非聚集,并且表上没有聚集索引,则使用聚集索引。
在主键约束中定义的所有列都必须定义为不为 Null。 如果未指定可为空性,则参与主键约束的所有列的可为空性设置为非空。
如果在 CLR 用户定义类型的列中定义主键,则该类型的实现必须支持二进制排序。
外键约束
外键 (FK) 是用于在两个表中的数据之间建立和加强链接的一列或多列的组合,可控制可在外键表中存储的数据。 在外键引用中,当包含一个表的主键值的一个或多个列被另一个表中的一个或多个列引用时,就在这两个表之间创建了链接。 这个列就成为第二个表的外键。
例如, Sales.SalesOrderHeader 表具有 Sales.SalesPerson 表的外键链接,因为销售订单和销售人员之间存在逻辑关系。 SalesOrderHeader 表中的 SalesPersonID 列与 SalesPerson 表的主键列匹配。 SalesOrderHeader 表中的 SalesPersonID 列是 SalesPerson 表的外键。 通过创建此外键关系,无法将 SalesPersonID 的值插入到 SalesOrderHeader 表中,除非它已存在于 SalesPerson 表中。
外键约束的索引
与主键约束不同,创建外键约束不会自动创建相应的索引。 但是由于以下原因,对外键手动创建索引通常是有用的:
当在查询中组合相关表中的数据时,经常在联接条件中使用外键列,方法是将一个表的外键约束中的一列或多列与另一个表中的主键列或唯一键列匹配。 索引使数据库引擎可以在外键表中快速查找相关数据。 但是,不需要创建此索引。 即使没有对两个相关表定义主键或外键约束,也可以对来自这两个表中的数据进行组合,但两个表间的外键关系说明已用其键作为条件对其进行了优化,以便组合到查询中。
对主键约束的更改可由相关表中的外键约束检查。
引用完整性
尽管外键约束的主要目的是控制可以存储在外键表中的数据,但它还可以控制对主键表中数据的更改。 例如,如果销售人员行从 Sales.SalesPerson 表中删除,并且销售人员 ID 用于 Sales.SalesOrderHeader 表中的销售订单,则两个表之间的关系完整性将中断:已删除的销售人员的销售订单在 SalesOrderHeader 表中孤立,没有链接到 SalesPerson 表中的数据。
外键约束防止这种情况发生。 如果这些更改使指向外键表中数据的链接无效,约束将强制实施引用完整性,从而保证不能对主键表中的数据进行更改。 如果尝试删除主键表中的行或更改主键值,则当已删除或更改的主键值对应于另一个表的外键约束中的值时,该作将失败。 若要成功更改或删除外键约束中的行,必须先在外键表中删除或更改外键数据,这会将外键链接到不同的主键数据。
级联引用完整性
通过使用级联引用完整性约束,你可以定义当用户试图删除或更新现有外键指向的键时,数据库引擎执行的操作。 可以定义以下级联操作。
“NO ACTION” 数据库引擎引发错误,父表中行的删除或更新动作将被回滚。
在父表中更新或删除该行时,引用表中会更新或删除级联对应的行。 如果 timestamp 列是外键或被引用的键的一部分,则无法指定 CASCADE。 不能为具有 INSTEAD OF DELETE 触发器的表指定 ON DELETE CASCADE。 不能为具有 INSTEAD OF UPDATE 触发器的表指定 ON UPDATE CASCADE。
SET NULL 在更新或删除父表中的相应行时,构成外键的所有值都设置为 NULL。 若要执行此约束,外键列必须可为空值。 无法为具有 INSTEAD OF UPDATE 触发器的表指定。
SET DEFAULT 如果更新或删除父表中的相应行,则构成外键的所有值都设置为其默认值。 若要执行此约束,所有外键列都必须有默认定义。 如果某个列可为空值,并且未设置显式的默认值,则将使用 NULL 作为该列的隐式默认值。 不能为具有 INSTEAD OF UPDATE 触发器的表指定。
可将 CASCADE、SET NULL、SET DEFAULT 和 NO ACTION 在相互存在引用关系的表上进行组合。 如果 数据库引擎 遇到 NO ACTION,它将停止并回滚相关的 CASCADE、SET NULL 和 SET DEFAULT 操作。 如果 DELETE 语句导致 CASCADE、SET NULL、SET DEFAULT 和 NO ACTION 操作的组合,则在 数据库引擎 检查所有 NO ACTION 前,将应用所有 CASCADE、SET NULL 和 SET DEFAULT 操作。
触发器和级联引用动作
级联引用操作以下列方式触发 AFTER UPDATE 或 AFTER DELETE 触发器:
首先执行由原始 DELETE 或 UPDATE 直接引发的所有级联引用操作。
如果在受影响的表上定义了任何 AFTER 触发器,这些触发器会在执行所有级联操作后触发。 这些触发器将按与级联操作相反的顺序激发。 如果单个表中有多个触发器,则它们会按随机顺序触发,除非该表有专用的第一个或最后一个触发器。 此顺序是使用 sp_settriggerorder指定的。
如果多个级联链源自一个表,而该表是 UPDATE 或 DELETE 操作的直接对象,则这些链触发其各自触发器的顺序是未指定的。 但是,只有当一条链激发其所有的触发器之后,另一条链才开始激发。
在表上的 AFTER 触发器是 UPDATE 或 DELETE 操作的直接目标时,无论是否影响任何行,它仍会触发。 在这种情况下,级联操作不会影响其他表。
如果上述任一触发器对其他表执行 UPDATE 或 DELETE 操作,这些操作可以启动次级级联链。 在所有主链上的触发器都触发之后,辅助链针对每个 UPDATE 或 DELETE 操作进行处理。 后续的 UPDATE 或 DELETE 操作可能会以递归方式重复此过程。
在触发器内执行 CREATE、ALTER、DELETE 或其他数据定义语言(DDL)操作可能会导致 DDL 触发器被激活。 这可能会随后执行 DELETE 或 UPDATE 操作,以启动其他级联链和触发器。
如果在任何特定的级联引用操作链中产生错误,则会引发错误,该链中不会触发 AFTER 触发器,并且回滚创建该链的 DELETE 或 UPDATE 操作。
具有 INSTEAD OF 触发器的表不能有指定级联操作的 REFERENCES 子句。 但是,级联作针对表的 AFTER 触发器可以对另一个表或视图执行 INSERT、UPDATE 或 DELETE 语句,该语句触发在该对象上定义的 INSTEAD OF 触发器。
相关任务
下表列出了与主键和外键约束关联的常见任务。
| 任务 | 主题 |
|---|---|
| 介绍如何创建主键。 | 创建主键 |
| 介绍如何删除主键。 | 删除主键 |
| 介绍如何修改主键。 | 修改主键 |
| 介绍如何创建外键关系 | 创建外键关系 |
| 介绍如何修改外键关系。 | 修改外键关系 |
| 介绍如何删除外键关系。 | 删除外键关系 |
| 介绍如何查看外键属性。 | 查看外键属性 |
| 介绍如何禁用复制的外键约束。 | 对复制禁用外键约束 |
| 介绍如何在 INSERT 或 UPDATE 语句期间禁用外键约束。 | 使用 INSERT 和 UPDATE 语句禁用外键约束 |