介绍
在设计数据库时,有时您可能希望限制某些列中允许的数据。例如,如果您要创建一个包含摩天大楼信息的表,您可能希望包含每个建筑物高度的列禁止负值。
关系数据库管理系统 (RDBMS) 允许您通过约束控制将哪些数据添加到表中。约束是适用于一个或多个列的特殊规则-或整个表- ,限制什么样的变化可以对表格数据进行,无论是通过的INSERT
,UPDATE
或DELETE
声明。
本文将详细回顾什么是约束以及它们如何在 RDBMS 中使用。它还将遍历 SQL 标准中定义的五个约束中的每一个,并解释它们各自的功能。
什么是约束?
在 SQL 中,约束是应用于列或表的任何规则,用于限制可以输入的数据。任何时候你试图执行一个操作,即改变在一个表中保存的数据-如INSERT
,UPDATE
或DELETE
声明-该RDBMS将测试数据是否违反了任何存在的制约因素,如果是这样,则返回一个错误。
数据库管理员通常依靠约束来确保数据库遵循一组定义的业务规则。在数据库的上下文中,业务规则是企业或其他组织遵循且其数据也必须遵守的任何政策或程序。例如,假设您正在构建一个数据库,用于对客户的商店库存进行编目。如果客户指定每个产品记录都应该有一个唯一的标识号,您可以创建一个带有UNIQUE
约束的列,以确保该列中没有两个条目是相同的。
约束也有助于维护数据完整性。数据完整性是一个广义的术语,通常用于根据特定用例描述数据库中保存的数据的整体准确性、一致性和合理性。数据库中的表通常密切相关,一个表中的列依赖于另一个表中的值。由于数据输入通常容易出现人为错误,因此约束在此类情况下很有用,因为它们可以帮助确保没有错误输入的数据会影响此类关系,从而损害数据库的数据完整性。
假设您正在设计一个包含两个表的数据库:一个用于列出一所学校的当前学生,另一个用于列出该学校篮球队的成员。您可以将FOREIGN KEY
约束应用于篮球队表中的列,该列引用了学校表中的列。这将通过要求团队表的任何条目引用学生表中的现有条目来建立两个表之间的关系。
用户在第一次创建表时定义约束,或者他们可以稍后使用ALTER TABLE
语句添加约束,只要它不与表中已有的任何数据冲突。创建约束时,数据库系统会自动为其生成名称,但在大多数 SQL 实现中,您可以为任何约束添加自定义名称。这些名称用于ALTER TABLE
在更改或删除语句时引用语句中的约束。
SQL 标准正式定义了五个约束:
PRIMARY KEY
FOREIGN KEY
UNIQUE
CHECK
NOT NULL
注意:许多 RDBMS 包含DEFAULT
关键字,用于定义列的默认值,NULL
除非在插入行时未指定值。其中一些数据库管理系统的文档DEFAULT
称为约束,因为它们的 SQL 实现使用DEFAULT
类似于约束的语法UNIQUE
或CHECK
。但是,从DEFAULT
技术上讲,这不是约束,因为它不限制可以将哪些数据输入到列中。
现在您已经对如何使用约束有了大致的了解,让我们仔细看看这五个约束中的每一个。
PRIMARY KEY
该
PRIMARY KEY
约束要求给定列中的每个条目都唯一且不唯一NULL
,并允许您使用该列来标识表中的每一行。
在关系模型中,键是表中的一列或一组列,其中每个值都保证唯一且不包含任何NULL
值。甲主键是一个特殊的键,其值用于在一个表来识别单独的行,并且包括该主键的一个或多个列可以被用于识别整个数据库中的其余部分的表。
这是关系数据库的一个重要方面:使用主键,用户不需要知道他们的数据在机器上的物理存储位置,他们的 DBMS 可以跟踪每条记录并临时返回它们。反过来,这意味着记录没有定义的逻辑顺序,用户可以按任何顺序或通过他们希望的任何过滤器返回他们的数据。
您可以使用PRIMARY KEY
约束在 SQL 中创建主键,它本质上是UNIQUE
和NOT NULL
约束的组合。定义主键后,DBMS 会自动创建与之关联的索引。索引是一种数据库结构,有助于更快地从表中检索数据。类似于教科书中的索引,查询只需查看索引列中的条目即可找到相关值。这就是允许主键充当表中每一行的标识符的原因。
一张表只能有一个主键,但与常规键一样,一个主键可以包含多个列。话虽如此,主键的一个定义特性是它们仅使用唯一标识表中每一行所需的最小属性集。为了说明这个想法,想象一个表,它使用以下三列存储有关学校学生的信息:
studentID
: 用来保存每个学生的唯一识别号firstName
: 用来保存每个学生的名字lastName
: 用来存放每个学生的姓氏
学校的一些学生可能会共享一个名字,这使得该firstName
列成为主键的一个糟糕选择。lastName
列也是如此。由 thefirstName
和lastName
列组成的主键可以工作,但仍有可能两个学生共享名字和姓氏。
由studentID
和firstName
或lastName
列组成的主键可以工作,但由于每个学生的标识号已经知道是唯一的,因此在主键中包含任何一个名称列都是多余的。因此,在这种情况下,可以标识每一行的最小属性集,因此对于表的主键来说是一个不错的选择,只是studentID
列本身。
如果键由可观察的应用程序数据(即表示现实世界实体、事件或属性的数据)组成,则称为自然键。如果密钥是在内部生成的并且不代表数据库之外的任何内容,则它被称为代理或合成密钥。一些数据库系统建议不要使用自然键,因为即使是看似恒定的数据点也可能以不可预测的方式发生变化。
FOREIGN KEY
该
FOREIGN KEY
约束要求给定列中的每个条目必须已经存在于另一个表的特定列中。
如果您有两个要相互关联的表,可以这样做的一种方法是使用FOREIGN KEY
约束定义外键。一个外键是在一个表(“子”表),其值来自于另一个表中的关键字(“父”)的列。这是表达两个表之间关系的一种方式:FOREIGN KEY
约束要求应用它的列中的值必须已经存在于它引用的列中。
下图突出显示了两个表之间的这种关系:一个用于记录有关公司员工的信息,另一个用于跟踪公司的销售情况。在这个例子中,EMPLOYEES
表的主键被表的外键引用SALES
:
如果尝试向子表添加一条记录,而输入到外键列的值在父表的主键中不存在,则插入语句将无效。这有助于维护关系级别的完整性,因为两个表中的行将始终正确关联。
通常,表的外键是父表的主键,但情况并非总是如此。在大多数 RDBMS 中,父表中应用了UNIQUE
orPRIMARY KEY
约束的任何列都可以被子表的外键引用。
UNIQUE
该
UNIQUE
约束禁止将任何重复值添加到给定列。
顾名思义,UNIQUE
约束要求给定列中的每个条目都是唯一值。任何尝试添加已出现在列中的值都将导致错误。
UNIQUE
约束对于强制表之间的一对一关系很有用。如前所述,您可以使用外键在两个表之间建立关系,但是表之间可以存在多种关系:
- 一对一:如果父表中的行与子表中的一行且只有一行相关,则称两个表具有一对一的关系
- one-to-many : 在多对多关系中,父表中的一行可以关联到子表中的多行,但是子表中的每一行只能关联到父表中的一行
- 多对多:如果父表中的行可以关联到子表中的多行,反之亦然,则称两者为多对多关系
通过向应用了UNIQUE
约束的列添加约束FOREIGN KEY
,您可以确保父表中的每个条目在子表中只出现一次,从而在两个表之间建立一对一的关系。
请注意,您可以UNIQUE
在表级别和列级别定义约束。在表级别定义时,UNIQUE
约束可以应用于多个列。在这种情况下,约束中包含的每一列都可以有重复的值,但每一行都必须在受约束的列中具有唯一的值组合。
CHECK
一个
CHECK
约束定义了一列,被称为一个要求断言,即进入到它的每一个值必须满足。
CHECK
约束谓词写在取值为表达式的形式TRUE
,FALSE
或可能UNKNOWN
。如果您尝试将一个值输入到具有CHECK
约束的列中,并且该值导致谓词的计算结果为TRUE
or UNKNOWN
(对于NULL
值会发生这种情况),则操作将成功。但是,如果表达式解析为FALSE
,它将失败。
CHECK
谓词通常依赖于数学比较运算符(如<
, >
, <=
, OR >=
)来限制允许进入给定列的数据范围。例如,CHECK
约束的一个常见用途是在负值没有意义的情况下防止某些列包含负值,如下例所示。
此CREATE TABLE
语句创建一个表,productInfo
其中包含每个产品名称、标识号和价格的列。因为产品的价格为负是没有意义的,所以此语句对列施加了CHECK
约束price
以确保它只包含正值:
- CREATE TABLE productInfo (
- productID int,
- name varchar(30),
- price decimal(4,2)
- CHECK (price > 0)
- );
并非每个CHECK
谓词都必须使用数学比较运算符。通常情况下,你可以使用任何SQL操作,可以评估到TRUE
,FALSE
或UNKNOWN
在一个CHECK
谓语,包括LIKE
,BETWEEN
,IS NOT NULL
,和其他人。一些 SQL 实现(但不是全部)甚至允许您在CHECK
谓词中包含子查询。但是请注意,大多数实现不允许您在谓词中引用另一个表。
NOT NULL
该
NOT NULL
约束禁止将任何NULL
值添加到给定列。
在大多数 SQL 实现中,如果您添加一行数据但未为特定列指定值,则数据库系统默认将丢失的数据表示为NULL
. 在 SQL 中,NULL
是一个特殊关键字,用于表示未知、缺失或其他未指定的值。然而,NULL
它本身并不是一个值,而是一个未知值的状态。
为了说明这种差异,假设有一个表格用于跟踪人才代理机构的客户,该表格包含每个客户的名字和姓氏的列。如果客户使用单名——比如“Cher”、“Usher”或“Beyoncé”——数据库管理员可能只在名字列中输入单名,导致 DBMSNULL
在姓列中输入。数据库不会将客户的姓氏视为字面上的“空”。这只是意味着该行的姓氏列的值未知或该字段不适用于该特定记录。
顾名思义,NOT NULL
约束防止给定列中的任何值成为NULL
。这意味着对于具有NOT NULL
约束的任何列,您必须在插入新行时为其指定一个值。否则,INSERT
操作将失败。
结论
对于希望设计具有高级数据完整性和安全性的数据库的任何人来说,约束都是必不可少的工具。通过限制输入到列中的数据,您可以确保正确维护表之间的关系,并且数据库遵守定义其目的的业务规则。
有关如何创建和管理 SQL 约束的更多详细信息,您可以查看我们关于如何在 SQL 中使用约束的指南。如果您想了解更多关于 SQL 的一般信息,我们鼓励您查看我们关于如何使用 SQL 的系列。