数据库的完整性指的是数据的正确性和相融性。数据的正确性是指符合现实世界语意、反映当前实际情况;数据的相容性指的是数据库对同一对象在不同关系表中的数据是符合逻辑的,比如学生的学号一定是唯一的,学生所属的专业一定是专业表中有的专业等,数据的完整性主要是防止数据库中出现不合语义的数据。为了维护数据库的完整性,DBMS需要实现以下功能:
- 提供定义完整性约束条件的机制
完整性约束条件又称完整性规则,是数据库中的数据必须满足的语意约束条件。表达了给定数据模型中数据以及其联系具有的约束和依存规则。比如主键和外键。 - 提供完整性检查的方法
DBMS检查数据是否满足完整性约束条件的机制称为完整性检查,一般会在插入、更新和删除操作后开始执行检查。 - 进行违约处理
DBMS如果发现用户操作违背了完整性约束将会采取一定动作,比如拒绝(NO ACTION)此操作或者级联(CASCADE)执行其他操作。
关系型数据库管理系统使得完整性控制称为其核心支持的功能,从而能够为所有用户和应用提供一致的数据库完整性。
1.1 实体完整性
关系模型的实体完整性在CREATE TABLE中使用PRIMARY KEY定义。实体完整性指的是是否能够唯一的区分出一个实体,因此PRIMARY KEY是用于唯一标识一个实体的符号,如同人的身份证一般。PRIMARY KEY可以只使用一个属性作为主键,也可以将多个属性设置为主键。
使用了PRIMARY KEY定义主码之后,对基本表进行更新或者插入操作的时候,就会执行实体完整性规则检查,包括:
- 检查主码值是否唯一,不唯一则拒绝操作
- 检查主码的各个属性是否为空,只要有某一行的属性为空就拒绝修改
- 检查主码是否为空的唯一方法就是进行全表扫描,判断是否有重复的主码。但是遍历的性能很低,因此DBMS会在主码上自动建立一个索引,比如B+树索引,通过索引查找基本表中是否存在新的主码值,这样会大大提高效率。
1.2 参照实体性
关系模型的参照完整性在CREATE TABLE中用FOREIGN KEY来定义哪些列为外码,用REFERENCES短语指明这些外码参照哪些表的主码。
参照完整性将两个表中的相应元组联系起来了,因此对被参照表和参照表进行CRUD时可能会破坏参照完整性,必须进行检查以保证这两个表数据的一致性。比如:
当发生不一致的情况,一般有以下三种处理策略:
- 拒绝(NO ACTION)执行:不允许该语句执行,这是默认策略
- 级联(CASCADE):假设删除或者修改被参照表的一个元组导致参照表不一致时,删除或者修改参照表中所有导致不一致的元组。比如删除了学生表中的一个学生,则顺带在选课表表中删除该学生的所有选课信息。
- 设为空值:可以将被参考表不一致的数据暂时置为空值,比如删除了班级表中的一个班级,然后在学生表中将该班级的所有学生的班级信息都变为空值,等待重新分班。该策略要注意:参照表中的的列需要设置为允许空值。
1.3 用户定义的完整性
用户定义的完整性就是针对某一具体应用的数据必须满足的语义要求。目前DBMS都提供了定义和检验这类完整性的机制。
1.3.1 属性上的约束条件
在CREATE TABLE定义属性的同时,可以根据应用要求定义属性上的约束条件,也就是属性值限制,包括:
- 列值非空(NOT NULL)
- 列值唯一(UNIQUE)
- 检查列值是否满足一个条件表达式(CHECK短语)
使用例子如下:
CREATE TABLE Student
(Sno CHAR(9) PRIMARY KEY,
Ssex CHAR(2) CHECK (Ssex IN ('m', 'f')) // 性别只能是男或者女
如果不满足属性约束条件,会被直接拒绝执行
1.3.2 元组上的约束条件
元组上的约束条件定义和属性约束条件定义类型,在CREATE TABLE语句中可以使用CHECK短语定义元组上的约束条件。元组级的约束条件可以设置不同属性取值的相互约束条件。
// 学生性别为男的时候,名字不能以Ms.打头
CREATE TABLE Student
(Sno CHAR(9) PRIMARY KEY,
Sname CHAR(8) NOT NULL,
Ssex CHAR(2),
CHECK (Ssex=‘f’ OR Sname NOT LIKE ‘Ms.%’));
如果不满足元组约束条件规则,会被直接拒绝执行
1.4 完整性约束命名子句
SQL在CREATE TABLE语句中提供了完整性约束命名子句CONSTRAINT,用来对完整性约束条件命名,从而可以更灵活的增加、删除一个完整性约束条件,格式如下:
CONSTRAINT <完整性约束条件名><完整性约束条件>
其中的完整性约束条件包括NOT NULL, UNIQUE, PRIMARY KEY, FOREIGN KEY,CHECK短语等等。通过给约束命名,我们可以很方便的修改完整性,比如:
ALTER TABLE Student DROP CONSTRAINT C4; //去除Student表中的C4约束
ALTER TABLE Student ADD CONSTRAINT C1 CHECK (Sno BETWEEN 9000 AND 9999); // 新增C1约束
1.5 域中的完整性限制
1.6 断言
当一个约束涉及 2 个(或)更多的表时,表约束机制有时很难,结果可能不会如预期的那样。为了涵盖这种情况,SQL 支持创建断言,这些断言是不与一张表关联的约束。断言语句应确保数据库中始终存在某个条件。每当在相应的表中进行修改时,DBMS 总是检查断言。
在SQL中可以使用CREATE ASSERTION语句,通过声明性断言来指定更具一般性的约束。可以定义涉及多个表或者聚集操作的比较复杂的完整性约束。断言创建后,任何对断言中所涉及到的关系的操作都会触发关系数据库对断言的检查,任何使断言不为真的操作都会被拒绝执行。
一般使用如下语句创建断言:
CREATE ASSERTION <断言名><CHECK子句>
每个断言都会被赋予一个名字,CHECK子句中的约束条件和WHERE子句的条件表达式类似。
// 限制数据库课程最多60名学生选修
CREATE ASSERTION ASSE_SC_DB_SUM CHECK (60 >= (SELECT COUNT(*) FROM Course, SC WHERE SC.Cno = Course.Cno AND Course.Cname = '数据库'));
每当往SC表中插入一条元组,该断言就会被触发。一旦人数超过60人,CHECK子句就会返回false值。
删除断言只需要使用DROP ASSERTION <断言名>
1.7 触发器(Trigger)
触发器是用户定义在关系表上的一类由事件驱动的特殊过程。用户的对应操作会触发触发器,而触发器则会执行相应的操作。
1.7.1 定义触发器
触发器又叫做事件——条件——动作规则。当特定的系统事件发生的时候,则会对规则的条件进行检查。创建触发器的指令如下:
CREATE TRIGGER <触发器名>
{BEFORE|AFTER} <触发事件> ON <表名>
REFERENCING NEW|OLD ROW AS <变量>
FOR EACH{ROW|STATEMENT}
[WHEN <触发条件>] <触发动作体>
- 只有表的拥有者,才可以在表上创建触发器,并且一个表上只能创建一定数量的触发器
- 触发器名可以包含模式名。同一模式下,触发器名必须是唯一的,并且触发器名和表名需要在同一模式下。
- 触发器只能定义在基本表上,不能定义在视图上。
- 触发事件可以是INSTER\DELETE\UPDATE和这几个事件的组合。AFTER、BEFORE指明了触发的时机,是执行前还是执行后触发。
- 触发器类型可以被分为行级触发器(FOR EACH ROW)和语句级触发器(FOR EACH STATEMENT)。假设表Teacher有1000行,执行UPDATE Teacher SET Deptno=5,如果是语句级触发器,则执行完语句后执行一次;如果是行级触发器,则会执行1000次触发体。
- 触发条件:触发器被激活是,只有触发条件为真触发动作体才会开始执行
- 触发动作体:
触发动作体指的是触发触发器后执行的动作。触发动作体可以是一个匿名的PL/SQL过程块,也可以是对已经创建存储过程的调用。如果触发动作体执行失败,则激活触发器的事件会终止执行。
1.7.2 激活触发器
1.7.3 删除触发器
使用
DROP TRIGGER <触发器名> ON <表名>
删除触发器
1.7.4 断言和触发器的区别
编号 | 断言 | 触发器 |
---|---|---|
1 | 当知道给定的特定条件总是为真时,可以使用断言。 | 即使特定条件可能是也可能不是,可以使用触发器 |
2 | 当SQL条件不满足时,整个表甚至数据库都有可能被锁定。 | 如果查询条件不成立,触发器可以捕获错误。 |
3 | 断言没有链接到特定的表或事件。它执行用户指定或定义的任务。 | 它有助于维护数据库表中的完整性约束,尤其是在未定义主键和外键约束时。 |
4 | 断言不维护对表中所做更改的任何跟踪。 | 触发器跟踪表中发生的所有更改。 |
5 | 与触发器相比,断言的语法更小。 | 它们有很大的语法来指示创建的触发器的每一个特定的。 |
6 | 现代数据库不使用断言。 | 触发器在现代数据库中得到了很好的应用。 |
断言不能修改数据,它们不链接到数据库中的任何特定表或事件,但触发器更强大,因为它们可以检查条件并修改数据库内表中的数据,这与断言不同。 |