我们在设计数据库时,每张表、每一列,都会给它明确的定义并规划了用途。但设计时我们规划好了,使用时就是喜欢有人捣乱,胡乱写一些不符合最初设计目的数据进来,该怎么避免呢?。就比如我们往学生考试成绩表中写s_id字段时,写一个学生信息表中不存在的学生s_id编号进来,也是可以成功写入的,但这并不符合我们的预期。
如何才能保证数据的完整性及可靠性呢?按照一般的控制手法(类比初中时学的如何消除噪音),我一时间大概可以想到三种:
SQL中的约束,则是第二种方式,在写入过程中进行判断。
SQL中的约束是针对字段级别来进行限制的,可以在创建表的同时进行字段的约束声明,或者在表创建后补上约束。需要注意的是,如果创建后已有不符合想要加上的约束的数据,则无法直接添加约束,需处理掉这部分数据后再添加约束。
常用SQL的约束主要有以下六类:
关键词 | 作用 |
---|---|
NOT NULL | 非空约束 |
UNIQUE | 唯一值约束 |
PRIMARY KEY | 主键约束 |
FOREIGN KEY | 外键约束 |
CHECK | 检查约束 |
DEFAULT | 默认值约束 |
看了命名和简单描述应该就大致知道它的作用了,接下来详细介绍下。
NOT NULL,见名知意,就是该字段不能为null空值,如果在写入数据时没用给该字段赋值,则无法写入。以学生信息表为例,设置classes字段不能为空:
create table t_student
(s_id number,
name varchar2(20) ,
age number,
gender varchar2(10),
classes varchar2(5) not null -- not null,非空
);
往该表中插入数据时,如果未给classes指定值或值为空,均不准插入,会提示ORA-01400错误:
--未指定值
insert into t_student(s_id,name,age,gender) values(1,'小虎',20,'male');
--指定值为null
insert into t_student(s_id,name,age,gender,classes) values(1,'小虎',20,'male',null);
约束的影响这么大,分分钟给报错的,每个约束没有自己的名字岂不是很没排面?来,给整上,用constraint来指定约束名:
create table t_student
(s_id number,
name varchar2(20) ,
age number,
gender varchar2(10),
classes varchar2(5) constraint nn_student_classes not null
);
约束名也有命名规范,具体看每个公司的要求,清晰的约束名可以在报错时快速定位到问题点,nn_student_classes就代表:非空+表明+字段。
唯一约束,用来限制该列的值只能出现一次,如果其他行中的这一列,存在值相同的列,则不允许本次的插入或修改。学生信息表中的学生ID编号就应该是这样的,那么:
create table t_student
(s_id number constraint UN_student_id unique,
name varchar2(20) ,
age number,
gender varchar2(10),
classes varchar2(5) constraint NN_student_classes not null
);
有了唯一约束后,如果我们插入两条id相同的数据,则第二条无法插入成功,会报错ORA-00001:unqiue constraint(UN_student_id),我们通过约束名UN_student_id就可以很快定位到问题点:
--第一条数据插入成功
insert into t_student(s_id,name,age,gender,classes) values(1,'小虎',20,'male','A');
--第二条数据s_id相同,插入失败
insert into t_student(s_id,name,age,gender,classes) values(1,'小猪',18,'male','A');
除了insert,在进行更新update时也需要遵守约束条件,如果新值已存在,则无法更新。
我们也可以在表声明的末尾来声明约束(NOT NULL约束不支持):
create table t_student
(s_id number ,
name varchar2(20) ,
age number,
gender varchar2(10),
classes varchar2(5) constraint NN_student_classes not null,
constraint UN_student_id unique (s_id)
);
需要注意的是,如果我们分别s_id和name创建唯一约束,应该写为:
create table t_student
(s_id number ,
name varchar(20) ,
age number,
gender varchar(10),
classes varchar(5) constraint NN_student_classes not null,
constraint UN_student_id unique (s_id),
constraint UN_student_name unique (name)
);
如果我们同时s_id和name创建唯一约束,则表示是一个联合唯一约束:
create table t_student
(s_id number ,
name varchar2(20) ,
age number,
gender varchar2(10),
classes varchar2(5) constraint NN_student_classes not null,
constraint UN_student_id_name unique (s_id,name)
);
联合唯一约束的意思是:被约束的所有列的值同时存在,才不满足该约束,只要有一个列的值尚未存在,即可正常操作。如果我们插入两条数据,第一条id为1,name为“小虎”,第二条id依旧为1,只要name有变化,依旧是可以插入的:
--第一条数据插入成功
insert into t_student(s_id,name,age,gender,classes) values(1,'小虎',20,'male','A');
--第二条数据s_id相同,但name不同,依旧插入成功
insert into t_student(s_id,name,age,gender,classes) values(1,'小猪',18,'male','A');
这样的结果明显不符合我们该表设计的初衷,所以在建表时需要注意别偷懒,该写两行的就得写两行。
联合唯一约束使用于单个字段无法确定唯一性,多个字段(可以不止两个哦)联合起来看才是唯一的情况,实际开发中比较少见。
主键约束,基本上每张表设计时都需要一个主键,即常说的id,一张表中只能有一个主键。可以将主键约束看作:NOT NULL + UNIQUE + 主键索引。主键必须是非空且唯一的,并且oracle会对主键列创建创建一个索引。
create table t_student
(s_id number ,
name varchar2(20) constraint NN_student_name not null,
age number,
gender varchar2(10),
classes varchar2(5) constraint NN_student_classes not null,
constraint PK_student_id primary key (s_id)
);
一张表中只能有一个主键。如果同时写了多个字段,那么是创建了一个联合主键,与联合唯一约束效果类似。
外键约束,名字看上去与主键约束类似,但作用却大不相同。
当我们有多张表,且表之间有关联时,可以使用外键约束来限制数据的值。如:学生分数表的学生id字段,就必须在学生信息表中存在对应id,我们可以通过references来在列明后直接声明约束:
create table t_score
(s_id number references t_student(s_id),
score number,
course varchar2(20)
);
更推荐的还是在表后使用constraint关键字的方式:
create table t_score
(s_id number,
score number,
course varchar2(20),
constraint FK_score_id foreign key (s_id) references t_student(s_id)
);
如果t_student表中只有两条学生记录,s_id分别为1和2,则:
--外键列在父列中有对应值,则插入成功
insert into t_score(s_id,score,course) values(1,98,'语文');
insert into t_score(s_id,score,course) values(2,88,'语文');
--外键列在父列中无对应值,则插入失败
insert into t_score(s_id,score,course) values(3,95,'语文');
通过外键约束,则可以保证子表的值父表中一定存在,数据完整性得以保障。
但是,并不建议在实际开发过程中过多使用外键约束,特别是数据库表结构错综复杂的系统。如果存在一层一层的外键约束,那么对父表进行操作时,往往由于子表的约束而导致不能操作。我们更偏向于采用方法一:在写入时判断,来对这种约束情况进行控制。
检查约束,用于限制列的值在规定范围以内。开发过程中也不太会使用这种约束,更多的是通过代码逻辑来判断。
以检查学生信息表的age年龄在0到150岁之间,为例:
create table t_student
(s_id number ,
name varchar2(20) constraint NN_student_name not null,
age number,
gender varchar2(10),
classes varchar2(5) constraint NN_student_classes not null,
constraint PK_student_id primary key (s_id),
constraint CK_student_age check(age between 0 and 150)
);
在插入或更新age时,若不符合CK_student_age约束范围,则无法更改操作。
DEFAULT约束用于向列中插入默认值,如果没有指明具体的值,那么会将默认值添加到所有的新记录。
例如,如果不给出具体值,我们默认学生性别为男性:
create table t_student
(s_id number ,
name varchar2(20) constraint NN_student_name not null,
age number,
gender varchar2(10) default 'male', --默认值
classes varchar2(5) constraint NN_student_classes not null,
constraint PK_student_id primary key (s_id)
);
及时当插入数据时未指定gender的值,也会自动补为’male’。
在表创建后,也可以继续对表的字段增加或删除约束。
通过ALTER TABLE并ADD方式,增加约束:
ALTER TABLE t_student
ADD CONSTRAINT un_student_id UNIQUE(s_id);
需要注意的是,同一数据库中的约束名不能重名哦。
通过ALTER TABLE并DROP方式,删除约束:
ALTER TABLE t_student
DROP CONSTRAINT un_student_id;
确保被约束名在数据库中存在哦。