第 1 页 共 18 页
Oracle 约束详解
作者:太虚野老
第 2 页 共 18 页
说明:
⚫ 不要直接执行附带的 SQL 文件,单独执行 SQL 文件中的 SQL 语句。
⚫ 本文所使用的数据库版本为 Oracle12c。
一、 约束简介
约束是一个或多个为了保证数据完整性而实现的一套机制,约束是数据库服务器
强制用户必须遵从的业务逻辑。它们限制用户只能输入指定范围的值,从而强制引用
完整性。
约束可以定义在字段级别和表级别,根据约束的类别的不同指定约束定义的不同
位置,在 Oracle 中最常使用的约束分为如下 5 类:
⚫ 非空约束:字段的值不能为空,一般在字段级别使用 NOT NULL 列属性进行
约束。
⚫ 唯一约束:指定列的值在整个表的相同列中是唯一的,既可以在表级别也可
以在字段级别定义,在字段级别使用 UNIQUE 进行声明。
⚫ 检查约束:在定义数据库表时,在字段级别或在表级别加入检查约束,使其
满足特定的要求,允许指定字段的检查条件,比如值大于 0 或小于 0 等。
⚫ 主键约束:SQL 92 建议在建立一个表时定义一个主键,它其实就是:唯一约
束+非空约束。
⚫ 外键约束:用于定义表间关联的约束,实现数据完整性,这是关系型数据库
的精髓。
在 Oracle12c 中,非空约束属于检查约束。
约束可以在创建表的时候定义,也可以在建表之后使用 ALTER 语句添加和修改约束。
二、 创建主键约束
当使用 CREATE TABLE 语句创建表时,列的 NOT NULL 和 UNIQUE 关键字都是表列的
约束,这些约束限制了在列中可以存储的数据的类型,除此之外,还可以在列类型的后面
使用 PRIMARY KEY 关键字指定列的类型为主键。当为列指定了主键后,列被强制为 NOT
NULL,并且列中的每行都被强制为一个唯一值,此外,会根据这个列自动地创建一个索
引。
例如,对于 invoice 这个表,可以使用如下的语法来创建并指定主键。
创建 invoice 表并指定主键
CREATE TABLE invoice
(
invoice_id NUMBER PRIMARY KEY, --自动编号,唯一,不为空
vendor_id NUMBER NOT NULL, --供应商 ID
invoice_number VARCHAR2(50) NOT NULL, --发票编号
invoice_date DATE DEFAULT SYSDATE, --发票日期
invoice_total NUMBER(9,2) NOT NULL, --发票总数
payment_total NUMBER(9,2) DEFAULT 0 --付款总数
)
注意:如果 invoice 表已经存在,则使用 DROP TABLE invoice 语句将其删除重建。
代码中使用 PRIMARY KEY 关键字对 invoice_id 列进行修饰,表示将以 invoice_id 作为
表的主键,这是最简单的指定表的主键的方式,但不是最好的编程习惯。
注意:如果没有为约束指定名称,Oracle 将使用 SYS_Cn 格式自动生成一个名称,其
中 n 表示一个唯一性的整数,可以通过 USER_CONSTRAINTS 数据字典表来了解特定的表
第 3 页 共 18 页
定义的约束。
建议的方法是在列或或表级别使用 CONSTRAINT 关键字,为约束指定一个约束名,因
而对于创建表的代码也可以使用如下的语法:
在列属性中使用 CONSTRAINT 关键字
CREATE TABLE invoice
(
invoice_id NUMBER CONSTRAINT invoice_pk PRIMARY KEY, --自动编号,
唯一,不为空
vendor_id NUMBER CONSTRAINT vendor_id_nn NOT NULL, --供应商 ID
invoice_number VARCHAR2(50) CONSTRAINT vendor_number_nn NOT NULL, --发
票编号
invoice_date DATE DEFAULT SYSDATE, --发票日期
invoice_total NUMBER(9,2) CONSTRAINT invoice_total_nn NOT NULL, --
发票总数
payment_total NUMBER(9,2) DEFAULT 0 --付款总数
)
通过将 CONSTRAINT 定义在列类型的后面,可以显示的创建约束,并能为约束指定约
束名称。对于 UNIQUE 与 PRIMARY KEY,还可以在表级别使用 CONSTRAINT 指定约束,比
如在为一个表设置多个主键时,可以在表级别使用 CONSTRAINT 设置约束。在表级别与在
列级别的效果是相同的,但是能提供更清晰的代码,如下所示:
在表级别使用 CONSTRAINT 关键字
CREATE TABLE invoice
(
invoice_id NUMBER , --自动编号,唯一,不为空
vendor_id NUMBER, --供应商 ID
invoice_number VARCHAR2(50), --发票编号
invoice_date DATE DEFAULT SYSDATE, --发票日期
invoice_total NUMBER(9,2) , --发票总数
payment_total NUMBER(9,2) DEFAULT 0, --付款总数
CONSTRAINT invoice_pk PRIMARY KEY (invoice_id),
CONSTRAINT vendor_id_un UNIQUE (vendor_id)
);
上述代码相对于列类型来说最大的好处在于可以使用多列,比如在括号内输入以逗号
分隔的多个列名,可以同时指定多列主键,例如如果要使用 invoice_id 和 vendor_id 作为主
键,可以使用如下所示的代码:
CONSTRAINT invoiceid_vendorid_pk PRIMARY KEY (invoice_id, vendor_id)
在为表设计主键时,下面是一些常用的设置规则:
⚫ 主键应该是对用户没有意义的,在一些数据表的设计中,不建议以材料编码
或身份证号码及员工工号作为主键,主键应该只是一些具有唯一性标识的标
识符,比如自增长的数字等。
⚫ 主键应该是单列的,以便提高连接和筛选操作的性能,复合主键通常导致不
良的外键,因此要尽量避免。
⚫ 主键应该是不能被更新的,主键的主要作用是唯一标识一行,更新则违反了
主键无意义的原则。
第 4 页 共 18 页
⚫ 主键不应该包含动态更新的数据,比如时间戳、创建时间或修改时间等这些
动态变化的数据。
⚫ 主键最好由计算机自动生成,在 Oracle 中可以使用序列来为主键列生成值。
三、 创建外键约束
外键约束又称为引用约束,这种类型的约束主要用来在多个表之间定义关系,并强制
引用完整性,与主键约束一样,外键约束也可以在列级别和表级别创建,使用关键字
REFERENCES 语句来定义,列级别的外键约束语法如下所示:
[ CONSTRAINT constraint_name ]
REFERENCES table_name (column_name)
[ ON DELETE {CASCADE | SET NULL } ]
位于[ ]的可选部分指定 CONSTRAINT 和约束名称,ON DELETE {CASCADE | SET NULL }
这行代码指示是否级联删除,当两个表中的两个字段建立了外键关联后,如果主键所在表
中的值被删除,使用 ON DELETE 指定是否级联删除,CASCADE 表示关联表中的内容一并
删除,而 SET NULL 表示子表中的值设置为 NULL。
注意:如果没有指定 ON DELETE,默认情况下将使用 CASCADE 进行级联删除。
假定有一个表 vendors,可以将 invoice 表的 vendor_id 与 vendors 表的 vendor_id 进
行外键约束,也就是说,invoice 表中的字段取值必须是 vendors 表中已经存在的供应商字
段,vendors 表的创建代码如下所示:
vendors 表的创建代码
CREATE TABLE vendors
(
vendor_id NUMBER, --供应商 id
vendor_name VARCHAR2(50) NOT NULL, --供应商名称
CONSTRAINT vendors_pk PRIMARY KEY (vendor_id), --主键
CONSTRAINT vendor_name_uq UNIQUE (vendor_name) --唯一性约束
)
下面的代码创建 invoice 表,在列级别为 invoice 表的 vendor_id 字段与 vendors 表的
vendor_id 字段进行了关联,代码如下所示:
在 invoice 表中为 vendor_id 列创建外键关联
CREATE TABLE invoice
(
invoice_id NUMBER , --自动编号,唯一,不为空
vendor_id NUMBER REFERENCES vendors (vendor_id), --供应商 ID
invoice_number VARCHAR2(50), --发票编号
invoice_date DATE DEFAULT SYSDATE, --发票日期
invoice_total NUMBER(9,2) , --发票总数
payment_total NUMBER(9,2) DEFAULT 0, --付款总数
CONSTRAINT invoiceid_vendorid_pk PRIMARY KEY (invoice_id,vendor_id),
CONSTRAINT vendor_id_un UNIQUE (vendor_id)
);
通过使用 REFERENCES 语法,指定要关联的目标表名与字段,示例中指定 vendors 表
的主键 vendor_id 列作为关联字段。
注意:在定义外键时,引用的表键必须是唯一性键值,一般建议使用关联表的主键作
为关联字段。
第 5 页 共 18 页
同样可以在表级别使用 CONSTRAINT 关键字来创建外键约束,例如下面的代码在表级
别使用 CONSTRAINT 定义了外键关联并指定了 ON DELETE 级联删除设置,代码如下所
示:
在 invoice 表级别创建外键关联
CREATE TABLE invoice
(
invoice_id NUMBER , --自动编号,唯一,不为空
vendor_id NUMBER, --供应商 ID
invoice_number VARCHAR2(50), --发票编号
invoice_date DATE DEFAULT SYSDATE, --发票日期
invoice_total NUMBER(9,2) , --发票总数
payment_total NUMBER(9,2) DEFAULT 0, --付款总数
CONSTRAINT invoiceid_vendorid_pk PRIMARY KEY (invoice_id,vendor_id),
CONSTRAINT vendor_id_un UNIQUE (vendor_id),
CONSTRAINT invoice_fk_vendors FOREIGN KEY (vendor_id) REFERENCES vendors
(vendor_id)
ON DELETE CASCADE
);
使用 CONSTRAINT 语法的不同之处在于需要为外键指定一个名称;使用 FOREIGN KEY
指定外键字段;REFERENCES 指定关联表和关联字段;上述代码使用 ON DELETE 显式指定
了级联删除特性。
所谓外键关系,就是一个表中的列引用了其它表中的列。例如 invoice 表中的
vendor_id 列引用了 vendors 表中的 vendor_id 列。vendors 表称为父表,而 invoice 表则称
为子表,这是因为 invoice 表中的 vendor_id 列依赖于 vendors 表中的 vendor_id 列。
如果试图向 invoice 表中插入一行,但指定的 vendor_id 在 vendors 表中不存在,数据
库就会返回错误。这个错误说明数据库无法找到匹配的父键值(此处父键就是 vendors 表
中的 vendor_id 列)。
下面向 vendors 表中插入两条记录,插入语句如下所示:
INSERT INTO vendors VALUES(1,'路人甲供应商');
INSERT INTO vendors VALUES(2,'路人乙供应商');
接下来向 invoice 表中插入三条记录:
INSERT INTO invoice VALUES(1,1,'0001',NULL,100,100); --插入成功
INSERT INTO invoice VALUES(1,2,'0001',NULL,100,100); --插入成功
INSERT INTO invoice VALUES(1,3,'0001',NULL,100,100); --插入失败
因为 invoice 表中的 vendor_id 字段具有一个外键约束,关联到 vendors 表的主键
vendor_id 列,因此第 3 条语句向 invoice 表中插入一条在 vendors 表中不存在的 vendor_id
字段值时,Oracle 提示插入失败。
再来看看级联删除。在上述代码中,已经为 invoice 表中的 vendor_id 外键设置了级联
删除特性,因此当从 vendors 表中删除记录时,与该 vendor_id 相关的 invoice 表中的相关
记录也会被删除。例如执行如下代码删除 vendors 表中 vendor_id 为 1 的供应商 id 号:
DELETE FROM vendors WHERE vendor_id = 1;
执行删除前两个表的数据如下所示: