数据库完整性
数据库的完整性是指数据的正确性和相容性。
- 数据的正确性:数据符合现实世界语义、反映当前实际状况。
- 数据的相容性:数据库同一对象在不同关系表中的数据是符合逻辑的。
为维护数据库的完整性,数据库管理系统必须能够实现如下功能:
- 提供定义完整性约束条件机制
- 提供完整性检查的方法
- 进行违约处理
实体完整性
定义实体完整性
关系模式的实体完整性在 CREATE TABLE 中用 PRIMARY KEY 定义。
例:将Student表中的Sno属性定义为码。
CREATE TABLE Student
(Sno CHAR(9) PRIMARY KEY, /*单属性主码,可在列级定义主码*/
Sname CHAR(20) NOT NULL,
Ssex CHAR(2),
Sage SMALLINT,
Sdept CHAR(20)
);
或
CREATE TABLE Student
(Sno CHAR(9),
Sname CHAR(20) NOT NULL,
Ssex CHAR(2),
Sage SMALLINT,
Sdept CHAR(20),
PRIMARY KEY (Sno) /*单属性主码,也可在表级定义主码*/
);
例:将SC表中的Sno、Cno属性组定义为主码。
CREATE TABLE SC
(Sno CHAR(9) NOT NULL,
Cno CHAR(4) NOT NULL,
GRADE SMALLINT,
PRIMARY KEY (Sno,Cno) /*多属性主码,只能在表级定义主码*/
);
实体完整性检查和违约处理
确实关系的主码后,当进行了插入或更新操作时,DBMS 将按照实体完整性规则进行自动检查。
- 检查主码值是否唯一,如果不唯一则拒绝插入或修改。
- 检查主码的各个属性是否为空,只要有一个为空就拒绝插入或修改。
检查主码是否唯一的方法有:全盘扫描和索引。
参照完整性
定义参照完整性
关系模型的参照完整在 CREATE TABLE 中用 FOREIGN KEY 短语定义那些列为外码,用 REFERENCES 短语知名这些外码参照那些表的主码。
例:定义SC中参照完整性。
CREATE TABLE SC
(Sno CHAR(9) NOT NULL,
Cno CHAR(4) NOT NULL,
Grade SMALLINT,
PRIMARY KEY (Sno,Cno), /*表级定义实体完整性*/
FOREIGN KEY (Sno) REFERENCES Student(Sno), /*表级定义参照完整性*/
FOREIGN KEY (Cno) REFERENCES Course(Cno) /*表级定义参照完整性*/
);
参照完整性检查和违约处理
被参照表(例:Student) | 参照表(例:SC) | 实例 | 违约处理 |
---|---|---|---|
可能破坏参照完整性 | 插入元组 | SC 表中增加一个元组,该元组的 Sno 在 Student 表中找不到 | 拒绝 |
可能破坏参照完整性 | 修改外码值 | 修改 SC 表中的一个元组,修改后的元组的 Sno 在 Student 表中找不到 | 拒绝 |
删除元组 | 可能破坏参照完整性 | 从 Student 表中删除一个元组,造成 SC 表中的某些元组的 Sno 在 Student 表中找不到 | 拒绝/级联删除/设为空值 |
修改主码值 | 可能破坏参照完整性 | 修改 Student 表中的一个元组的 Sno,造成 SC 表中的某些元组的 Sno 在 Student 表中找不到 | 拒绝/级联删除/设为空值 |
一般地,当对参照表和被参照表地操作违反参照完整性时,系统选用默认策略,也就是拒绝执行。如果想让系统采用其他策略就必须在创建参照表时显式地加以说明。
例:显式地说明参照完整性的违约处理。
CREATE TABLE SC
(Sno CHAR(9),
Cno CHAR(4),
Grade SMALLINT,
PRIMARY KEY (Sno,Cno), /*表级定义实体完整性,Sno、Cno均不能为空值*/
FOREIGN KEY (Sno) REFERENCES Student(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 /*当更新Course表中的Cno时,级联更新SC表中相应的元组*/
);
用户定义的完整性
用户定义的完整性就是针对某一具体应用的数据必须满足的语义要求。
属性上的约束条件
属性上约束条件的定义
在 CREATE TABLE 中定义属性的同时,可以根据应用要求定义属性上的约束条件:
列值非空(NOT NULL)
例:在定义SC表时,说明Sno、Cno、Grade属性不允许取空值。 CREATE TABLE SC (Sno CHAR(9) NOT NULL, /*Sno不允许为空值*/ Cno CHAR(4) NOT NULL, Grade SMALLINT NOT NULL, PRIMARY KEY (Sno,Cno), /*表级定义实体完整性,隐含了Sno、Cno不允许为空值,列级相应的定义可不写*/ ⋮ );
列值唯一(UNIQUE)
例:建立部门表DEPT,要求部门名称Dname列取值唯一,部门编号Deptno列为主码。 CREATE TABLE DEPT (Deptno NUMERIC(2), Dname CHAR(9) UNIQUE NOT NULL, /*要求Dname列值唯一,且不能为空*/ Location CHAR(10), PRIMARY KEY (Deptno) );
检查列值是否满足一个条件表达式(CHECK 短语)
例:Student表的Ssex只允许取“男”或“女”。 CREATE TABLE Student (Sno CHAR(9) PRIMARY KEY, Sname CHAR(8) NOT NULL, Ssex CHAR(2) CHECK(Ssex IN('男','女')), /*性别属性Ssex只允许取'男'或'女'*/ Sage SMALLINT, Sdept CHAR(20) ); 例:SC表的Grade的值应该在0到100之间。 CREATE TABLE SC (Sno CHAR(9), Cno CHAR(4), Grade SMALLINT CHECK(Grade>=0 AND Grade<=100), /*Grade取值范围是0到100*/ PRIMARY KEY (Sno,Cno) FOREIGN KEY (Sno) REFERENCES Student(Sno), FOREIGN KEY (Cno) REFERENCES Course(Cno) );
属性上约束条件的检查和违约处理
当往表中插入元组或修改属性的值时,RDBMS 将检查属性上的约束条件是否被满足,如果不满足操作将会被拒绝执行。
元组上的约束条件
元组上约束条件的定义
在 CREATE TABLE 语句中可以用 CHECK 短语定义元组上的约束条件。
同属性值限制相比,元组级的限制可以设置不同属性之间的取值的相互约束条件。
例:当学生的性别是男时,其名字不能以Ms.打头。
CREATE TABLE Student
(Sno CHAR(9),
Sname CHAR(8) NOT NULL,
Ssex CHAR(2),
Sage SMALLINT,
Sdept CHAR(20),
PRIMARY KAY (Sno),
CHECK(Ssex='女' OR Sname NOT LIKE 'Ms.%') /*定义了元组中Sname和Ssex两个属性值之间的约束条件*/
);
元组上约束条件的检查和违约处理
当往表中插入元组或修改属性的值时,RDBMS 将检查元组上的约束条件是否被满足,如果不满足操作将会被拒绝执行。
完整性约束命名子句
在 CREATE TABLE 语句中还可以用 CONSTRAINT 子句对完整性约束条件命名,从而灵活的增加,删除一个完整性约束条件。
完整性约束命名子句
CONSTRAINT <完整性约束条件名><完整性约束条件>
例:建立学生登记表Student,要求学号在90000~99999之间,姓名不能取空值,年龄小于30,性别只能是“男”或“女”。
CREATE TABLE Student
(Sno NUMERIC(6)
CONSTRAINT C1 CHECK(Sno BETWEEN 90000 AND 99999),
Sname CHAR(20)
CONSTRAINT C2 NOT NULL,
Sage NUMERIC(3)
CONSTRAINT C3 CHECK(Sage<30),
Ssex CHAR(2)
CONSTRAINT C4 CHECK(Ssex IN('男','女')),
CONSTRAINT StudentKey PRIMARY KEY(Sno)
);
例:建立教师表TEACHER,要求每个教室的应发工资不低于3000元。应发工资是工资列Sal与扣除项Deduct之和。
CREATE TABLE TEACHER
(Eno NUMERIC(4) PRIMARY KEY,
Ename CHAR(10),
Job CHAR(8),
Sal NUMERIC(7,2),
Deduct NUMERIC(7,2),
Deptno NUMERIC(2),
CONSTRAINT TEACHERFKey FOREIGN KEY(Deptno) REFERENCES DEPT(Deptno),
CONSTRAINT C1 CHECK(Sal+Deduct>=3000)
);
修改表中的完整性限制
可以使用 ALTER TABLE 语句修改表中的完整性限制。
例:去掉Student表中对性别的限制。
ALTER TABLE Student
DROP CONSTRAINT C4;
例:修改Student表中的约束条件,要求学号改为在900000~999999之间,年龄由小于30改为小于40.
思路:先删除原来的约束条件,在增加新的约束条件。
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);
域中的完整性限制
在 SQL 中,可以用 CREATE DOMAIN 语句建立一个域以及该域应该满足的完整性约束条件,然后就可以使用域来定义属性了。
例:建立一个性别域,并声明性别域的取值范围。
CREATE DOMAIN GenderDomain CHAR(2)
CHECK (VALUE IN('男','女'));
这样对Ssex的寿命可以写为:
Ssex GenderDomain
例:建立一个性别域GenderDomain,并对其中的限制命名。
CREATE DOMAIN GenderDomain CHAR(2)
CONSTRAINT GD CHECK (VALUE IN('男','女'));
例:删除域GenderDomain的限制条件GD。
ALTER DOMAIN GenderDomain
DROP CONSTRAINT GD;
例:在域GenderDomain上增加性别的限制条件GDD。
ALTER DOMAIN GenderDomain
ADD CONSTRAINT GDD CHECK(VALUE IN('1','0'));
断言
在 SQL 中可以使用数据定义语言中的 CREATE ASSERTION 语句,通过声明性断言(declarative assertions)来指定更具一般性的约束。可以定义涉及多个表或聚集操作的比较复杂的完整性约束。断言创建以后,任何对断言中所涉及关系的操作都会触发关系数据库管理系统对断言的检查,任何使断言不为真值的操作都会被拒绝执行。
创建断言的语句格式
CREATE ASSERTION <断言名><CHECK子句>
每个断言都被赋予一个名字,<CHECK子句> 中的约束条件与 WHERE 子句的条件表达式类似。
例:限制数据库课程最多60名学生选修。
CREATE ASSERTION ASSE_SC_DB_NUM
CHECK(60>=(SELECT count(*) /*此断言的谓词涉及聚集操作count的SQL语句*/
FROM Course,SC
WHERE SC.CNO=COURSE.CNO AND COURSE.CNAME='数据库'
)
);
例:限制每一门课程最多60名学生选修。
CREATE ASSERTION ASSE_SC_CNUM1
CHECK(60>=ALL(SELECT count(*) /*此断言的谓词,涉及聚集操作count*/
FROM SC /*和分组函数group by的SQL语句*/
GROUP by cno
)
);
例:限制每个学期每一门课程最多60名学生选修。
首先修改SC表的模式,增加一个“学期(TERM)”的属性。
ALTER TABLE SC
ADD TERM DATE; /*首先修改SC表,增加TERM属性,它的类型是DATE*/
然后定义断言:
CREATE ASSERTION ASSE_SC_CNUM2
CHECK(60>=ALL(SELECT count(*)
from SC
group by cno,TERM
)
);
删除断言的语句格式
DROP ASSERTION <断言名>;
触发器
触发器(trigger)是用户定义在关系表上的一类由事件驱动的特殊过程。一旦定义,触发器将被保存在数据库服务器中。任何用户对表的增、删、改操作均由服务器自动激活相应的触发器,在 RDBMS 核心层进行集中的完整性控制。
定义触发器
触发器又叫做事件—条件—动作(event-condition-action)规则。当特定的系统事件(如对一个表的增、删、改操作,事物的结束等)发生时,对规则的条件进行检查,如果条件成立则执行规则中的动作,否则不执行该动作。规则中的动作体可以很复杂,可以涉及其他表和其他数据库对象,通常是一段 SQL 存储过程。
SQL 使用 CREATE TRIGGER 命令建立触发器,其一般格式为:
CREATE TRIGGER <触发器名> /*每当触发事件发生时,该触发器被激活*/
{BEFORE|AFTER} <触发事件> ON <表名> /*指明触发器激活事件是执行触发事件的前或后*/
REFERENCING NEW|OLD ROW AS<变量> /*REFERENCING 指出引用的变量*/
FOR EACH {ROW|STATEMENT}/*定义触发器的类型,指明动作执行的频率*/
[WHEN <触发条件>]<触发动作体>/*仅当触发条件为真时才执行触发动作体*/
只有表的拥有者即创建表的用户才可以创建触发器,并且一个表只能创建一定数量的发出器。
触发器名:触发器名可以包含模式名,也可以不包含模式名。同一模式下,触发器名必须是唯一的,并且触发器名和表名必须在同一模式下。
表名:触发器只能定义在基本表上,不能定义在视图上。当基本表的数据发生变化时,将激活定义在该表上的相应触发事件的触发器,因此该表也称为触发器的目标表。
触发事件:触发事件可以是
INSET
、DELETE
、UPDATE
,也可以是这几个事件的组合。还可以是UPDATE OF <触发列,···>
,即进一步指明修改哪些列时激活触发器。BEFORE|AFTER
是触发的时机。AFTER
表示触发事件的操作执行之后触发激活器,BEFORE
表示触发事件执行之前激活触发器。触发器类型:触发器按照所触发动作的间隔尺寸可以分为行级触发器(FOR EACH ROW)和语句级触发器(FOR EACH STATEMENT)。
例:在TEACHER表上创建一个AFTER UPDATE触发器,触发事件是UPDATE语句。 UPDATE TEACHER SET Deptno=5; 假设表TEACHER有1000行。 如果是语句级触发器,那么执行完该语句后,触发动作体只执行一次。 如果是行级触发器,触发动作体将执行1000次。
触发条件:触发器激活时,只有触发条件为真时触发动作体才执行,否则不执行。如果省略
WHEN
,则触发器激活时触发体立即执行。触发动作体:触发动作体既可以是一个匿名
PL\SQL
过程块,也可以是对已创建存储过程的调用。如果是行级触发器,用户都可以在过程体中使用NEW
和OLD
引用UPDATE/INSERT
事件之后的新值和UPDATE/DELETE
事件之前的旧值;如果是语句级触发器,则不能在触发动作体中使用NEW
或OLD
进行引用。 如果触发动作体执行失败,激活触发器的事件就会终止执行,触发器的目标表或触发器可能影响的其他对象不发生任何变化。例:当对表SC的Grade属性进行修改时,若分数增加了10%,则将此次操作记录到另一个表SC_U(Sno,Cno,Oldgrade,Newgrade)中,其中Oldgrade是修改前的分数,Newgrade是修改后的分数。 CREATE TRIGGER SC_T /*创建触发器SC_T*/ AFTER UPDATE OF Grade ON SC /*UPDATE OF Grade ON SC是出发时间*/ /*AFTER表示触发事件的操作执行之后激活触发器,即表SC的grade属性修改后再触发下面的规则*/ REFERENCING OLD row AS OldTuple, NEW row AS NewTuple FOR EACH ROW /*触发器类型为行级触发器*/ WHEN (NewTuple.Grade >= 1.1*OldTuple.Grade) /*触发条件*/ INSERT INTO SC_U(Sno,Cno,OldGrade,NewGrade) VALUES(OldTuple.Sno,OldTuple.Cno,OldTuple.Grade,NewTuple.Grade)
激活触发器
触发器的执行是由触发事件激活,并由数据库服务器自动执行。一个数据表上可能有一个或多个触发器,它们激活时遵循如下执行顺序:
- 执行该表上的 BEFORE 触发器。
- 激活触发器的 SQL 语句。
- 执行该表上的 AFTER 触发器。
一般来讲如果一个表上多个 BEFORE(AFTER)触发器,那么执行顺序规则是“谁先创建谁先执行”。但也有些 RDBMS 是按照触发器名字字母的排序执行触发器。
删除触发器
删除触发器的 SQL 语法如下:
DROP TRIGGER <触发器名> ON <表名>
触发器必须是一个已经创建的触发器,并且只能由具有相应权限的用户删除。