数据库的完整性(integrity)是指数据的正确性(correctness)和相容性(compat- ability)。
- 数据的正确性:指数据是符合现实世界语义、反映当前实际状况的
- 数据的相容性:指数据库同一对象在不同关系表中的数据是符合逻辑的
文章目录
[5.1 实体完整性](#5.1 实体完整性)
[5.1.1 定义实体完整性](#5.1.1 定义实体完整性)
[5.1.2 实体完整性检查和违约处理](#5.1.2 实体完整性检查和违约处理)
[5.2 参照完整性](#5.2 参照完整性)
[5.2.1 定义参照完整性](#5.2.1 定义参照完整性)
[5.2.2 参照完整性检查和违约处理](#5.2.2 参照完整性检查和违约处理)
[5.3 用户定义的完整性](#5.3 用户定义的完整性)
[5.3.1 属性上的约束条件](#5.3.1 属性上的约束条件)
[5.3.2 元组上的约束条件](#5.3.2 元组上的约束条件)
[5.4 完整性约束命名子句](#5.4 完整性约束命名子句)
[5.5 断言](#5.5 断言)
[5.6 触发器](#5.6 触发器)
[5.6.1 定义触发器](#5.6.1 定义触发器)
[5.6.2 激活触发器](#5.6.2 激活触发器)
[5.6.3 删除触发器](#5.6.3 删除触发器)
数据的完整性和安全性是两个既有联系又不尽相同的概念:
- 数据的完整性是为了防止数据库中存在不符合语义的数据,也就是防止数据库中存在不正确的数据。
防范对象:不合语义的、不正确的数据,防止它们进入数据库。
- 数据的安全性是保护数据库防止恶意破坏和非法存取。
防范对象:非法用户和非法操作,防止他们对数据库数据的非法存取。
为维护数据库的完整性,数据库管理系统必须:
1.提供定义完整性约束条件的机制
- 完整性约束条件也称为完整性规则,是数据库中的数据必须满足的语义约束条件。
- SQL 标准使用了一系列概念来描述完整性,包括关系模型的实体完整性、参照完整性和用户定义完整性。
- 这些完整性一般由 SQL 的数据定义语言语句来实现。
- 提供完整性检查的方法
- 数据库管理系统中检查数据是否满足完整性约束条件的机制称为完整性检查。
- 一般在 INSERT、UPDATE、DELETE 语句执行后开始检查,也可以在事务提交时检查。
- 进行违约处理
- 数据库管理系统若发现用户的操作违背了完整性约束条件,将采取一定的动作:拒绝(NO ACTION)执行该操作或级联(CASCADE)执行其他操作
关系数据库管理系统使得完整性控制成为其核心支持的功能,从而能够为所有用户和应用提供一致的数据库完整性。
5.1 实体完整性
5.1.1 定义实体完整性
- 关系模型的实体完整性在 CREATE TABLE 中用 PRIMARY KEY 定义。
- 单属性构成的码有两种说明方法:定义为列级约束条件、定义为表级约束条件
- 对多个属性构成的码只有一种说明方法:定义为表级约束条件
【例 5.1】将 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) /* 在表级定义主码 */
);
【例 5.2】将 SC 表中的 Sno、Cno 属性组定义为码。
CREATE TABLE SC
(Sno CHAR(9) NOT NULL,
Cno CHAR(4) NOT NULL,
Grade SMALLINT,
PRIMARY KEY (Sno,Cno) /*只能在表级定义主码*/
);
5.1.2 实体完整性检查和违约处理
用 PRIMARY KEY 短语定义了关系的主码后,每当用户程序对基本表插入一条记录或对主码列进行更新操作时,关系数据库管理系统按照实体完整性规则自动进行检查。包括:
(1)检查主码值是否唯一,如果不唯一则拒绝插入或修改。
(2)检查主码的各个属性是否为空,只要有一个为空就拒绝插入或修改。
检查记录中主码值是否唯一的一种方法是进行全表扫描,依次判断表中每一条记录的主码值与将插入记录的主码值 (或者修改的新主码值)是否相同。如下图所示。
全表扫描的缺点:十分耗时
为了避免对基本表进行全表扫描,关系数据库管理系统一般都在主码上自动建立一个索引。
5.2 参照完整性
5.2.1 定义参照完整性
关系模型的参照完整性定义
- 在 CREATE TABLE 中用 FOREIGN KEY 短语定义哪些列为外码
- 用 REFERENCES 短语指明这些外码参照哪些表的主码
例如,关系 SC 中一个元组表示一个学生选修的某门课程的成绩,(Sno,Cno)是主码,Sno、Cno 分别参照 Student 表的主码和 Course 表的主码。
【例 5.3】定义 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) /*在表级定义参照完整性*/
);
5.2.2 参照完整性检查和违约处理
参照完整性将两个表中的相应元组联系起来。
对被参照表和参照表进行增、删、改操作时有可能破坏参照完整性,必须进行检查以保证两个表的相容性。
例如,对表 SC 和 Student 有 4 种可能破坏参照完整性的情况:
(1)SC 表中增加一个元组,该元组的 Sno 属性值在表 Student 中找不到一个元组,其 Sno 属性值与之相等。
(2)修改 SC 表中的一个元组,修改后该元组的 Sno 属性值在表 Student 中找不到一个元组,其 Sno 属性值与之相等。
(3)从 Student 表中删除一个元组,造成 SC 表中某些元组的 Sno 属性值在表 Student 中找不到一个元组,其 Sno 属性值与之相等。
(4)修改 Student 表中一个元组的 Sno 属性,造成 SC 表中某些元组的 Sno 属性值在表 Student 中找不到一个元组,其 Sno 属性值与之相等。
当上述的不一致发生时,系统可以采用以下策略加以处理:
(1)拒绝(NO ACTION)执行:不允许该操作执行。该策略一般设置为默认策略。
(2)级联(CASCADE)操作:当删除或修改被参照表(Student)的一个元组导致与参照表(SC)的不一致时,删除或修改参照表中的所有导致不一致的元组。
(3)设置为空值:当删除或修改被参照表的一个元组时造成了不一致,则将参照表中的所有造成不一致的元组的对应属性设置为空值。
对于参照完整性,除了应该定义外码,还应定义外码列是否允许空值。
一般地,当对参照表和被参照表的操作违反了参照完整性时,系统选用默认策略,即拒绝执行。如果想让系统采用其他策略则必须在创建参照表时显示地加以说明。
【例 5.4】显式说明参照完整性的违约处理示例。
CREATE TABLE SC
(Sno CHAR(9) NOT NULL,
Cno CHAR(4) NOT NULL,
Grade SMALLINT,
PRIMARY KEY(Sno,Cno), /*在表级定义实体完整性,Sno,Cno 都不能取空值*/
FOREIGN KEY (Sno) REFERENCES Student(Sno) /*在表级定义参照完整性*/
ON DELETE CASCADE /*级联删除SC表中相应的元组*/
ON UPDATE CASCADE, /*级联更新SC表中相应的元组*/
FOREIGN KEY (Cno) REFERENCES Course(Cno) /*在表级定义参照完整性*/
ON DELETE NO ACTION /*当删除course 表中的元组造成与SC表不一致时拒绝删除*/
ON UPDATE CASCADE /*当更新course表中的cno时,级联更新SC表中相应的元组*/
);
关系数据库管理系统在实现参照完整性时,除了要提供定义主码、外码的机制外,还需要提供不同的策略供用户选择。具体选择哪种策略,要根据应用环境的要求确定。
5.3 用户定义的完整性
- 用户定义的完整性是:针对某一具体应用的数据必须满足的语义要求。
- 关系数据库管理系统提供了定义和检验用户定义完整性的机制,不必由应用程序承担。
5.3.1 属性上的约束条件
1. 属性上约束条件的定义
在 CREATE TABLE 中定义属性的同时,可根据应用要求定义属性上的约束条件,即属性值限制。包括:
- 列值非空(NOT NULL)
- 列值唯一(UNIQUE)
- 检查列值是否满足一个条件表达式(CHECK 短语)
(1)不允许取空值
【例 5.5】在定义 SC 表时,说明 Sno、Cno、Grade 属性不允许取空值。
CREATE TABLE SC
(Sno CHAR(9) NOT NULL,
Cno CHAR(4) NOT NULL,
Grade SMALLINT NOT NULL,
PRIMARY KEY (Sno, Cno), /*在表级定义实体完整性,隐含了Sno,Cno不允许取空值,
在列级不允许取空值的定义可不写 */
);
(2)列值唯一
【例 5.6】建立部门表 DEPT,要求部门名称 Dname 列取值唯一,部门编号 Deptno 列为主码。
CREATE TABLE DEPT
(Deptno NUMERIC(2),
Dname CHAR(9) UNIQUE NOT NULL, /*要求Dname列值唯一, 并且不能取空值*/
Location CHAR(10),
PRIMARY KEY (Deptno)
);
(3)用 CHECK 短语指定列值应该满足的条件
【例 5.7】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)
);
【例 5.8】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)
);
2. 属性上约束条件的检查和违约处理
当往表中插入元组或修改属性的值时,关系数据库管理系统将检查属性上的约束条件是否被满足,如果不满足则操作被拒绝执行。
5.3.2 元组上的约束条件
1. 元组上约束条件的定义
在 CREATE TABLE 语句中可以用 CHECK 短语定义元组上的约束条件,即元组级的限制。
同属性值限制相比,元组级的限制可以设置不同属性之间的取值的相互约束条件。
【例 5.9】当学生的性别是男时,其名字不能以 Ms. 打头。
CREATE TABLE Student
(Sno CHAR(9),
Sname CHAR(8) NOT NULL,
Ssex CHAR(2),
Sage SMALLINT,
Sdept CHAR(20),
PRIMARY KEY (Sno),
CHECK (Ssex='女' OR Sname NOT LIKE 'Ms.%')
); /*定义了元组中Sname和Ssex两个属性值之间的约束条件*/
2. 元组上约束条件的检查和违约处理
当往表中插入元组或修改属性的值时,关系数据库管理系统将检查元组上的约束条件是否被满足,如果不满足则操作被拒绝执行。
5.4 完整性约束命名子句
1. 完整性约束命名子句
完整性约束命名子句:
CONSTRAINT <完整性约束条件名> <完整性约束条件>
<完整性约束条件>
包括 NOT NULL、UNIQUE、PRIMARY KEY、FOREIGN KEY、CHECK 短语等。
【例 5.10】建立学生登记表 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)
);
【例 5.11】建立教师表 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)
);
2. 修改表中的完整性限制
使用 ALTER TABLE 语句修改表中的完整性限制。
【例 5.12】去掉例 5.10 Student 表中对性别的限制。
ALTER TABLE Student DROP CONSTRAINT C4;
【例 5.13】修改表 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);
5.5 断言
1. 创建断言的语句格式
CREATE ASSERTION <断言名>
每个断言都被赋予一个名字,中的约束条件与 WHERE 子句的条件表达式类似。
【例 5.18】限制数据库课程最多 60 名学生选修。
CREATE ASSERTIONASSE_SC_DB_NUM
CHECK (60>=(SELECT COUNT(*) /*此断言的谓词涉及聚集操作 count 的 SQL 语句*/
FROM Course,SC
WHERE SC.Cno=Course.Cno AND Course.Cname='数据库')
);
【例 5.19】限制每一门课程最多 60 名学生选修。
CREATE ASSERTION ASSE_SC_CNUM1
CHECK(60>=ALL (SELECT COUNT(*) /* 此断言的谓词,涉及聚集操作 count */
FROM SC /* 和分组函数 group by 的SQL语句 */
GROUP BY CNO)
);
【例 5.20】限制每个学期每一门课程最多 60 名学生选修。
/* 首先修改 SC 表的模式,增加一个"学期(TERM)"的属性。 */
ALTER TABLE SC ADD TERM DATE;
/* 然后定义断言 */
CREATE ASSERTION ASSE_SC_CNUM2
CHECK (60 >= ALL (SELECT COUNT(*) FROM SC GROUP BY CNO,TERM) );
2. 删除断言的语句格式
DROP ASSERTION <断言名>;
如果断言很复杂,则系统在检测和维护断言上的开销较高, 这是在使用断言时应该注意的。
5.6 触发器
触发器(trigger)是用户定义在关系表上的一类由事件驱动的特殊过程。
- 触发器保存在数据库服务器中
- 任何用户对表的增、删、改操作均由服务器自动激活相应的触发器,在关系数据库管理系统核心层进行集中的完整性控制
- 触发器类似于约束,但是比约束更加灵活,可以实施更为复杂的检查和操作,具有更精细和更强大的数据控制能力
注:不同的关系数据库管理系统实现触发器的语法各不相同、互不兼容。
5.6.1 定义触发器
- 触发器又叫做事件-条件-动作(event-condition-action)规则。
- 当特定的系统事件(如对一个表的增、删、改操作,事务的结束等)发生时,对规则的条件进行检查,如果条件成立则执行规则中的动作,否则不执行该动作。
- 规则中的动作体可以很复杂,可以涉及其他表和其他数据库对象, 通常是一段 SQL 存储过程。
建立触发器的一般格式:
CREATE TRIGGER <触发器名> /*每当触发事件发生时,该触发器被激活*/
{BEFORE|AFTER} <触发事件> ON <表名> /*指明触发器激活的时间是在执行触发事件前或后*/
REFERENCING NEW|OLD ROWAS<变量> /* REFERENCING 指出引用的变量*/
FOR EACH {ROW|STATEMENT} /* 定义触发器的类型,指明动作体执行的频率*/
[WHEN <触发条件>] <触发动作体> /* 仅当触发条件为真时才执行触发动作体*/
定义触发器的语法说明
(1)只有表的拥有者,即创建表的用户才可以在表上创建触发器,并且一个表上只能创建一定数量的触发器。
(2)触发器名
- 触发器名可以包含模式名,也可以不包含模式名。
- 同一模式下,触发器名必须是唯一的,且触发器名和表名必须在同一模式下。
(3)表名
- 触发器只能定义在基本表上,不能定义在视图上。
- 当基本表的数据发生变化时,将激活定义在该表上相应触发事件的触发器。
(4)触发事件
- 触发事件可以是 INSERT、DELETE 或 UPDATE,也可以是这几个事件的组合,还可以是 UPDATE OF <触发列,...>,即进一步指明修改哪些列时激活触发器。
- AFTER/BEFORE 是触发的时机,AFTER 表示在触发事件的操作执行之后激活触发器,BEFORE 表示在触发事件的操作执行之前激活触发器。
(5)触发器类型:行级触发器(FOR EACH ROW)和语句级触发器(FOR EACH STATEMENT)
例如,在例 5.11 的 TEACHER 表上创建一个 AFTER UPDATE 触发器,触发事件是 UPDATE 语句:
UPDATE TEACHER SET Deptno=5;
假设表 TEACHER 有 1000 行,如果定义的触发器为语句级触发器,那么执行完该语句后触发动作只发生一次;如果是行级触发器,触发动作将执行 1000 次。
(6)触发条件
- 触发器被激活时,只有当触发条件为真时触发动作体才执行,否则触发动作体不执行。
- 如果省略 WHEN 触发条件,则触发动作体在触发器激活后立即执行。
(7)触发动作体
- 触发动作体可以是一个匿名 PL/SQL 过程块,也可以是对已创建存储过程的调用。
- 如果是行级触发器,用户可以在过程体中使用 NEW 和 OLD 引用 UPDATE/INSERT 事件之后的新值和UPDATE/DELETE事件之前的旧值;如果是语句级触发器,则不能在触发动作体中使用 NEW 或 OLD 进行引用。
- 如果触发动作体执行失败,激活触发器的事件就会终止执行,触发器的目标表或触发器可能影响的其他对象不发生任何变化。
【例 5.21】当对表 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
OLDROW AS OldTuple,
NEWROW AS NewTuple
FOR EACH ROW /* 行级触发器,即每次执行一次 Grade 的更新,下面的规则就执行一次 */
WHEN (NewTuple.Grade >= 1.1*OldTuple.Grade) /* 触发条件,只有该条件为真时才执行 */
INSERT INTO SC_U(Sno,Cno,OldGrade,NewGrade) /* 下面的 INSERT 语句 */
VALUES(OldTuple.Sno,OldTuple.Cno,OldTuple.Grade,NewTuple.Grade)
【例 5.22】将每次对表 Student 的插入操作所增加的学生个数记录到表 StudentInsertLog 中。
CREATE TRIGGER Student_Count
AFTER INSERT ON Student /* 指明触发器激活的时间是在执行 INSERT 后 */
REFERENCING
NEW TABLE AS DELTA
FOR EACH STATEMENT /* 语句级触发器,即执行完 INSERT 语句后下面的触发动作体才执行一次 */
INSERT INTO StudentInsertLog (Numbers)
SELECT COUNT(*) FROM DELTA
【例 5.23】定义一个 BEFORE 行级触发器,为教师表 Teacher 定义完整性规则 "教授的工资不得低于 4000 元,如果低于 4000 元,自动改为 4000 元"。
CREATE TRIGGER Insert_Or_Update_Sal /*对教师表插入或更新时激活触发器*/
BEFORE INSERT OR UPDATE ON Teacher /* BEFORE触发事件*/
REFERENCING NEWrow AS NewTuple FOR EACH ROW /*行级触发器*/
BEGIN /*定义触发动作体,这是一个PL/SQL过程块*/
IF (newtuple.Job='教授') AND (newtuple.Sal < 4000)
THEN newtuple.Sal=4000;
END IF;
END; /*触发动作体结束*/
5.6.2 激活触发器
触发器的执行是由触发事件激活,并由数据库服务器自动执行的。
一个数据表上可能定义了多个触发器,同一个表上的多个触发器激活时遵循如下的执行顺序:
(1)执行该表上的 BEFORE 触发器
(2)激活触发器的 SQL 语句
(3)执行该表上的 AFTER 触发器
对于同一个表上的多个 BEFORE(AFTER)触发器,遵循"谁先创建谁先执行"的原则,即按照触发器创建的时间先后顺序执行。有些关系数据库管理系统是按照触发器名称的字母排序顺序执行触发器。
5.6.3 删除触发器
删除触发器的 SQL 语法:
DROP TRIGGER <触发器名> ON <表名>;
触发器必须是一个已经创建的触发器,并且只能由具有相应权限的用户删除。