第五章 数据库完整性
本文全部内容来自数据库系统概论(第5版)—王珊、萨师煊著
数据库完整性是指数据的正确性和相容性。
- 数据的正确性是指数据是符合显示世界语义、反映当前实际情况的
- 数据的相容性是指数据库同一对象在不同关系中的数据是符合逻辑的
数据库的完整性检查和控制的防范对象是不和语义的、不正确的数据。
目前主流数据库管理系统都支持如下功能:
- 提供定义完整性约束条件的机制
- 提供完整性检查的方法
- 进行违约处理
5.1 实体完整性
实体完整性主要由主码(主键 primary key
)控制。
5.1.1 定义实体完整性
关系型数据库中一般使用primary key
语句进行定义主码(主键)。用来特殊表示用户的唯一属性名。
例:将Student表中的学号属性定义为主码
create table Student(
Sno char(10) primary key,
Sname char(10) not null,
Ssex char(2),
Sage int,
Sdept char(20)
);
/*在创建表的同时对某些属性进行主键定义*/
用户的主键用来唯一标识一个实体,主键一般不能为空。
5.1.2 实体完整性检查和违约处理
当用户将表的某一个属性定义为主码后,系统会自动检查主码值是否唯一、被标记为主码的属性是否为空。
5.2 参照完整性
参照完整性主要由外码(外键 foreign key
)控制。
5.2.1 定义参照完整性
参照完整性在定义表时可以直接使用references
将外键与主键进行关联,表名这些外码参照哪些表的主码。
create table SC(
Sno char(10) not null,
Cno char(10) not null,
Grade int,
primary key(Sno,Cno),
foreign key(Sno) references Student(Sno),
foreign key(Cno) references Course(Cno)
);
/*在定义外码时一定要添加与外键关联的表*/
5.2.2 参照完整性和违约处理
用户在定义参照完整性时,对外码要定义是否允许空值,这样在进行违约处理时,外键也会被处理,否则在违反了参照完整性时,会默认拒绝执行。
create table SC(
Sno char(10),
Cno char(10),
Grade int,
primary key(Sno,Cno),
foreign key(Sno) references Studen(Sno)
on delete cascade /*当删除Student表中的元组时,级联删除SC表中相应的元组*/
on update cascade, /*当更新Student表中的Sno时,级联更新SC表中相应的元组*/
foreign key(Cno) references Course(Cno)
on delete no action /*当删除Course表中的元组造成与SC表不一致时,拒绝删除*/
on update cascade, /*当更新SC表中的Cno时,级联更新SC表中相应的元组*/
);
5.3 用户定义完整性
用户定义完整性时针对某一应用的数据必须满足的语义要求。
5.3.1 属性上的约束条件
1、属性上的约束条件
在创建表的同时,可以根据应用要求定义属性上的约束条件。
-
列值非空(
not null
)/*在定义用户表的同时,对某些属性进行非空定义*/ create table SC( Sno char(10) not null, Cno char(10) not null, Grade int not null, primary key(Sno,Cno), ... );
-
列值唯一(
unique
)/*创建部门表,要求部门名称取值唯一,部门标号Deptno为主码*/ create table dept( Deptno int(2) primary key, Dname char(10) unique not null, location char(10) );
-
检查列值是否满足一个条件表达式(
check
)/*检查性别属性是否满足条件*/ create table Student( Sno char(10) primary key, Sname char(10) not null, Ssex char(2) check(Ssex in ('男','女')), Sage int, Sdept char(20) );
2、属性上的约束条件检查和违约处理
当用户插入数据不满足属性上的约束条件时,语句则不会被执行。
5.3.2 元组上的约束条件
1、元组上约束条件的定义
元组级的限制可以设置不同属性之间取值的相互约束条件。
create table Student(
Sno char(10) primary key,
Sname char(10) not null,
Ssex char(2) check(Ssex in ('男','女')),
Sage int,
Sdept char(20),
check(Ssex='女' or name not like 'Ms.%')
);
2、元组上约束条件检查和违约处理
当用户插入数据不满足元组上的约束条件时,则语句不会被执行。
5.4 完整性约束命名子句
使用constraint
,用来对完整性约束条件命名,从而灵活增加、删除一个完整性约束条件。
1、完整性约束命名子句
constraint <完整性约束条件名> <完整性约束条件>
完整性约束条件包括:not null
unique
primary key
foreign key
check
等。
例:对学号,姓名,性别,年龄,主键进行完整性约束条件名定义。
create table Student(
Sno char(10) constraint c1 check(Sno between 90000 and 99999),
Sname char(10) constraint c2 not null,
Ssex char(2) conatraint c3 check(Ssex in ('男','女')),
Sage int constraint c4 check(Sage < 30),
constraint Studentkey primary key (Sno)
);
2、修改表中的完整性限制
可以使用alter table
语句修改表中的限制,删除使用delete
例:删除对性别的限制。
alter table Student drop constraint c4;
例:修改表中的约束条件限制。
alter table Student drop constraint c1;
alter table Student add constraint c1 check(Sno between 900000 and 999999);
alter table Student drop constraint c3;
alter table Student add constraint c3 check(Sage <40);
5.5 域中的完整性限制
域使用create domain
语句创建。
例:建立一个性别域,并声明性别域的取值范围
create domain genderDomain char(2) check(values('男','女'));
/*对限制命名*/
create domain genderDomain char(2) constraint gd check(values in ('男','女'));
对域的操作与对表的操作类似。
5.6 断言
断言是指更一般性的约束。
任何对断言中所涉及关系的操作都会触发关系数据库管理系统对断言的检查,任何使断言不为真值的操作都会被拒绝执行。
1、创建断言的语句格式
create assertion <断言名> <CHECK子句>
例:限制数据库课程最多60名学生选修。
create assertion asse_sc_db_num
check (60<=(
select count(*) from Course,SC
where SC.Cno=Course.Cno and Course.Cname='数据库'
));
例:限制每一门课程最多60名学生选修。
create assertion asse_SC_CNUM1
check (60>=All(
select count(*) from SC group by Cno
));
5.7 触发器
5.7.1 定义触发器
触发器又叫事件—条件—动作规则。
create trigger <触发器名> /*当触发事件发生时,该触发器激活*/
{before|after} <触发事件> on <表名> /*指明触发器激活的时间是在执行触发事件前或后*/
referencing new|old row as <变量> /*references支出引用的变量*/
for each{row|statement} /*定义触发器的类型,指明动作体执行的频率*/
[when<触发条件>]<触发动作体> /*仅当触发条件为真时才执行触发动作体*/
-
触发器只有表的拥有者才能创建,具体数量由具体数据库决定的。
-
统一模式下,触发器名必须是唯一的,并且触发器名和表名在统一模式下。
-
触发器只能定义在基本表上。
-
触发事件可以是单独的增、删、改事件,也可以是多个事件的组合。触发时机是在指定事件之前运行触发器还是之后运行,使用
after|before
区分。 -
触发器由行级触发器(
row
)和语句触发器(statement
)。例:在teacher表上创建一个after update触发器
update teacher set Deptno=5;
-
触发器的执行时在满足触发条件为真时才执行。
-
行级触发器,用户可以在过程体中使用
new|old
引用update|insert
事件之后的新值和update|delete
之前的旧值。语句触发器不能在触发动作体中使用
new|old
进行引用。
例:当对表SC的Grade属性进行修改时,若分数增加了10%,则将此次操作记录到另一个表中
/*SC_U(Sno,Cno,Oldgrade,Newgrade)*/
create trigger SC_T after update of Grade on SC
referencing OLDROW as OLDTUPLE,
NEWROW as NEWTUPLE
for each row
where (NEWTUPLE.Grade>=1.1*OLDTUPLE.Grade)
insert into SC_U(Sno,Cno,Oldgrade,Newgrade)
values(OLDTUPLE.Sno,OLDTUPLE.Cno,OLDTUPLE.Grade,NEWTUPLE.Grade)
5.7.2 激活触发器
当一个数据表上可能定义多个触发器是,其激活的原则是:
- 执行该表上的
before
触发器 - 激活触发器的
sql
语句 - 执行该表上的
after
触发器 - 对多个
before(after)
触发器,遵循实现创建谁先执行的原则
5.7.3 删除触发器
drop trigger <触发器名> on <表名>;
触发器的删除必须由已经创建的触发器,并且只能由具有相应权限的用户删除。