5.7 内置约束
在本章前面的内容中,已经涉及了约束的概念。比如说插入空值时,只可以对允许为空值的字段插入;例如更新雇员表时,如果设置的job_id的值不在职位表jobs中存在,则提示“违反检查约束条件”;再例如在删除部门表的某个部门时,雇员表中有这个部门的雇员,则提示“违反完整约束条件”,不允许删除。本节将详细地介绍约束的相关内容。
5.7.1 约束概述
Oracle数据库使用约束(constraints)来防止无效的数据进入表中,保护数据的实体完整性。约束规则定义在表级,如果表和表之间有从属关系,约束也可以防止表的删除。通过约束,强制用户在插入、更新或删除数据时必须遵循一定的规则。
约束可以在创建表的时候定义,也可以在创建表之后定义。所有的约束存储在数据字典中,可以用USER_CONSTRAINTS数据字典视图查看对一个表的约束的定义。
给约束起一个有意义的名字(例如DEPT_NAME_NN代表部门名不能为空的约束),易于约束的引用。如果不显式地命名约束,Oracle数据库将用格式SYS_Cn产生一个系统约束名字,这里的n 是一个唯一的整数,所以约束名是唯一的。
表5.3列举了一些常用的数据一致性约束。
表5.3 数据一致性约束
约 束 | 说 明 |
---|---|
NOT NULL | 指定字段不能是空值 |
UNIQUE | 指定字段的值(或字段组合的值)对于表中所有的行必须是唯一的 |
PRIMARY KEY | 指定表的每行的唯一性标识,即主键 |
FOREIGN KEY | 在指定字段和引用表的一个字段之间建立强制外键关系,即外键 |
CHECK | 指定一个必须为真的条件 |
5.7.2 约束使用
- 创建表时定义约束
下面给出了在创建表的同时定义约束的语法:
CREATE TABLE [schema.]table
(col1 type1 [DEFAULT1] [col1_constraint],
col2 type2 [DEFAULT2] [col2_constraint],
…
[table_constraint])
其中col1_constraint是一个给col1字段定义的完整性约束,而table_constraint是作为表table定义的完整性约束。
约束可以在两个级别上定义,分别是字段级别约束和表级别约束,其中字段级别约束值涉及一个单个的字段,能够定义完整性约束的任何类型;而表级别约束涉及一个或多个字段,不能定义NOT NULL这个约束。
假设现在需要新建一个部门表depts_temp3,该部门表有四个字段,分别是部门编号dept_id、部门名称dept_name、部门经理名字manager_fname和所在地dept_loc。其中部门编号和部门名称不能为空,并且部门编号是该表的主键(主键名为dept3_id_pk)。
其SQL语句如下:
CREATE TABLE depts_temp3
(dept_id NUMBER(4) NOT NULL,
dept_name VARCHAR2(20) NOT NULL,
manager_fname VARCHAR2(14),
dept_loc VARCHAR2(50)
CONSTRAINT dept3_id_pk PRIMARY KEY(dept_id)
);
执行该SQL语句,之后通过PL/SQL Dev查看该表的表结构和键,如图5.18和图5.19所示。
图5.18 创建表时定义约束
图5.19 创建表时定义主键
- NOT NULL约束
NOT NULL约束又称非空约束。NOT NULL约束只能定义在字段级别,不能定义在表级别。上面创建depts_temp3表的SQL语句中定义了两个NOT NULL约束,并且都是由系统产生约束名。如果需要指定DEPT_NAME字段的NOT NULL约束的名字为dept3_name_nn,其SQL语句如下(之前需要执行DROP TABLE depts_temp3删除表):
CREATE TABLE depts_temp3
(dept_id NUMBER(4) NOT NULL,
dept_name VARCHAR2(20) CONSTRAINT dept3_name_nn NOT NULL,
manager_fname VARCHAR2(14),
dept_loc VARCHAR2(50),
CONSTRAINT dept3_id_pk PRIMARY KEY(dept_id)
);
执行该SQL语句,再通过下面的SQL语句(注意DEPTS_TEMP3必须大写),从USER_CONSTRAINTS视图中获取关于depts_temp3表的约束信息,结果如图5.20所示。
SELECT * FROM user_constraints WHERE table_name = 'DEPTS_TEMP3'
图5.20 定义NOT NULL约束
从图5.20中可以看出,针对dept_id字段定义的约束因为没有显式命名,Oracle系统定义了SYS_C0011119的名字,另外两个约束都被显式地命名了。另外,没有显式命名的非空约束,使用PL/SQL Dev在Checks约束选项卡中是看不到的,这点需要注意。
- UNIQUE约束
UNIQUE约束又称唯一键约束,要求字段或者字段的组合(键)的每个值是唯一的,定义UNIQUE约束的字段(或字段组合)被称为唯一键(unique key)。对于无非空约束的字段,唯一键约束允许输入空值,且包含空值的行可以是任意数目,因为空不等于任何值。UNIQUE约束既可以在字段级也可以在表级定义,使用表级定义时,一个复合唯一键被创建。
如果要给depts_temp3表增加一个部门描述的字段dept_desc,且此字段是唯一键,键名为dept3_desc_uk,SQL语句如下:
ALTER TABLE depts_temp3
ADD(dept_desc VARCHAR(100) CONSTRAINT dept3_desc_uk UNIQUE)
该段SQL语句采用了在添加字段的同时给该字段定义约束的形式。执行该SQL语句,通过PL/SQL Dev查看该表的表结构和键,发现dept_desc字段被添加,且增加了一个唯一键约束。
- 主、外键约束
主键和外键是设置表和表之间关联的对象,在介绍多表查询时就是通过表的主键和外键的关系建立其表和表之间的连接。例如,部门表中部门编号是主键,雇员表中雇员的部门编号是外键,通过这一对主外键关系,将两个表建立关联。
PRIMARY KEY约束为表创建一个主键,每个表只能创建一个主键,主键是表的每一行的唯一性标识,该约束强制字段或字段组合的唯一性,并且确保作为主键一部分的字段不能包含空值。对于一个主键字段,UNIQUE索引被自动创建。主键可以定义在列级别,也可以定义在表级别,用表级别定义的主键通常是字段组合主键。
之前创建depts_temp3表时,通过“CONSTRAINT dept3_id_pk PRIMARY KEY (dept_id)”语句将dept_id字段定义为主键。需要注意的是,一个表只能定义一个主键约束,但同时可以有多个唯一键约束。
FOREIGN KEY约束是引用完整性约束,指明一个字段或者字段的组合作为一个外键,这个外键和另一个表的主键(或唯一键)建立起一个关系。在雇员表employees(依赖表或子表)中,部门编号字段department_id被定义为外键,它引用部门表departments(引用表或父表)中的部门编号字段department_id(主键)。
一个外键值必须匹配一个在父表中存在的值,所以在子表中,不能在外键字段中输入一个没有引用的值;同时在父表中,不能删除这样的行,该行的主键正被子表所引用。本节开始时提到的三个约束,其中后面两个约束就是这方面的主外键约束。
现在要创建一个临时雇员表emps,字段包括雇员编号emp_id、姓氏last_name、薪水salary和部门编号dept_id,其中部门编号dept_id是外键,引用depts_temp3表中的部门编号dept_id。
CREATE TABLE emps
(emp_id NUMBER(6) NOT NULL,
last_name VARCHAR2(25) NOT NULL,
salary NUMBER(8,2),
dept_id NUMBER(4),
CONSTRAINT emps_dept_fk FOREIGN KEY(dept_id) REFERENCES depts_temp3(dept_id),
CONSTRAINT emps_id_pk PRIMARY KEY(emp_id)
);
执行该SQL语句,之后通过PL/SQL Dev查看该表的键,如图5.21所示。
图5.21 主外键约束
- CHECK约束
CHECK约束是一种灵活的约束,定义每行都必须满足的条件。一个字段可以定义多个CHECK约束。
假设要求临时雇员表emps中的salary字段的值必须大于等于零,则可以通过下面的SQL语句,给emps中的salary字段添加约束。
ALTER TABLE emps
ADD CONSTRAINT emps_salary_min CHECK(salary >= 0)
该段SQL语句没有采用创建表时添加约束的形式,而是在创建表以后直接添加约束。其语法形式和修改表字段的语法形式类似,只是这里操作的是约束而不是字段。执行该SQL语句,之后通过PL/SQL Dev查看该表的Checks选项卡,其结果如图5.22所示。
图5.22 CHECK约束
- 添加约束
除了在创建表时给字段或表创建约束外,也可以直接给表添加、删除、启用或禁用约束。上面的例子就是采用了直接给表添加约束的形式。给表直接添加约束的语法形式如下:
ALTER TABLE table
ADD [CONSTRAINT cname] type(column)
其中cname是约束名,type是约束类型,column是受影响的字段名。约束名在语法中是一个可选项,如果不命名约束,系统会产生一个约束名,不过还是建议命名约束。
- 删除约束
删除约束的语法形式如下:
ALTER TABLE table
DROP PRIMARY KEY | UNIQUE(column) | CONSTRAINT cname [CASCADE]
为了删除约束,可以先从USER_CONSTRAINTS数据字典视图中确定约束的名字,然后使用带DROP子句的ALTER TABLE语句进行删除。DROP子句的CASCADE选项将会导致任何与其相依赖的约束也被删除。
例如要删除depts_temp3表中的dept3_desc_uk约束,其SQL语句如下:
ALTER TABLE depts_temp3
DROP CONSTRAINT dept3_desc_uk
例如要删除depts_temp3表中的主键约束(dept_id),并且删除相关联的emps表中dept_id字段上的外键约束,SQL语句如下:
ALTER TABLE depts_temp3
DROP PRIMARY KEY CASCADE
执行该SQL语句,之后通过PL/SQL Dev查看depts_temp3表的主键和emps表的外键,主外键均被删除。
l 启用或禁用约束
作为软件开发人员,启用或禁用约束很少用到,这里只列出它们的语法形式,读者有所了解即可。
禁用约束的语法形式如下:
ALTER TABLE table
DISABLE CONSTRAINT cname [CASCADE]
启用约束的语法形式如下:
ALTER TABLE table
ENABLE CONSTRAINT cname