数据库的完整性
- 数据的正确性
- 是指数据是符合现实世界语义,反映了当前实际情况的
- 数据的相容性
- 是指数据库同一对象在不同关系表中的数据是符合逻辑,例如学生的学号必须唯一,性别只能是男或女等
数据的完整性和安全性是两个不同的概念
- 数据的完整性
- 防止数据库中存在不符合语义的数据,也就是防止数据库中存在不正确的数据
- 防范对象:不合语义的,不正确的数据
- 数据的安全性
- 保护数据库,防止恶意的破坏和非法存取
- 防范对象:非法用户和非法操作
为维护数据库的完整性,数据库管理系统必须
- 提供定义完整性约束条件的机制
- 完整性约束条件也成为完整性规则,是数据库中的数据必须满足的语义约束条件
- SQL标准使用了一系列概念来描述完整性,包括关系模型的实体完整性,参照完整性和用户定义完整性
- 这些完整性一般由SQL的数据定义语言语句来实现
- 提供完整性检查的方法
- 数据库管理系统中检查数据是否满足完整性约束条件的机制称为完整性检查
- 一般在insert,update,delete语句执行后开始检查,也可以在事务提交时检查
- 违约处理
- 数据库管理系统若发现用户的操作违背了完整性约束条件,就采取一定的动作
- 拒绝(no action)执行该操作
- 级联(cascade)执行其他操作
- 数据库管理系统若发现用户的操作违背了完整性约束条件,就采取一定的动作
数据库语言不分大小,数据库中的操作语言一般为大写,这里做笔记为了防止看的不方便,因此用小写,因为我英语不好,用大写反应不过来
实体完整性
实体完整性定义
关系模型的实体完整性
create table 中用primary key定义主码,主码唯一且不为空值
单属性构成的码有两种说明方法
- 定义为列级约束条件
- 定义为表级约束条件
对多个属性构成的码只有一种说明方法
- 定义为表级约束条件
在创建表student的时候,因为Sno是单个属性构成的码,Sno是唯一主码,所以可以用列级完整性约束条件,也可以用表级完整性约束条件,用表级约束性条件就是将第二行的primary key删除,在第九行加上语句primary key (Sno)。
在创建表sc的时候,有两个外码Sno和Cno,这两个外码都是表sc的主码,属于多个属性构成的码,因此只能用表级完整性约束条件
实体完整性检查和违约处理
检查和违约处理由DBMS核心做,不需要用户写代码进行检查和违约处理
因为实体完整性约束条件只针对主码,因此每当用户对表进行包含主码列的增删改操作的时候,关系数据库管理系统按照实体完整性规则自动进行检查,包括:
- 检查主码值是否唯一,如果不唯一则拒绝操作
- 检查主码的各个属性是否为空,只要有一个为空,就拒绝操作
因为在表sc中,Sno和Cno都属于主码,因此Sno和Cno都需要值唯一且不为空,在上面的操作中,插入的数据中Cno为空,违背了实体完整性约束条件,所以拒绝操作。
检查记录总主码值是否唯一的一种方法是进行全表扫描
- 依次判断表中每一条记录的主码值与将插入记录上的主码值(或者修改的新主码值是否相同)
缺点——十分耗时
为了避免对基本表进行全表扫描,RDBMS核心一般都在主码上自动建立一个索引
B+树索引
B+树索引的根节点在内存中,所以根节点不用IO,所以上图中的IO只要两次
参照完整性
参照完整性定义
关系模型的参照完整性定义
在createtable中用foreign key短语定义外码,用references短语指明这些外码参照哪些表的主码
参照完整性检查和违约处理
一个参照完整性将两个表中的相应元组联系起来
对被参照表和参照表进行增删改操作的时候有可能破坏参照完整性,必须进行检查
参照完整性顾名思义,就是要参照其他的属性,既然是参照别的属性列,那么就应该为那个属性列的子集,以表sc为例,破坏参照完整性的情况有四种:
- 因为插入在表sc中的Sno的值不在表student的属性列Sno中,破坏了了参照完整性约束条件,因此拒绝插入操作
- 因为修改后在表中的Sno的值不在表student的属性列Sno中,破坏了参照完整性约束条件,因此拒绝修改操作
- 在MySQL中删除操作不需要级联,在表student中删除一个元组,那么表sc中Sno和表student中Sno值相等的元组自动被删除,但是在Kingbase中不可以,在Kingbase中,删除了表bfs中bfname为叶瑄的元组,那么在表yessai中yename为叶瑄就找不到参照列了,因此破坏了参照完整性约束条件,拒绝此删除操作。
- 因为将表student中Sno的值从201215121修改成20190306238之后,表sc中Sno值为201215121在表student找不到参照列了,破坏了参照完整性约束条件,因此拒绝修改操作。
可能破坏参照完整性的情况及违约处理
参照完整性违约处理
- 拒绝(no action)执行
- 不允许该操作执行。该策略一般设置为默认策略
- 级联(cascade)操作
- 当删除或修改被参照表的一个元组造成了与参照表的不一致,则删除或修改参照表中的素有造成不一致的元组
- 设置空值
- 当删除或修改被参照表的一个元组时造成了不一致,则将参照表中的所有造成不一致的元组的对应属性设置为空值,但是这里的外码不能是主码,因为主码唯一且不为空值,例如表sc中的Sno或Cno就不能设置为空值,因为Sno和Cno都是表sc的外码,例如在表course中,Cpno是外码,Cno是主码,同时也是Cpno的参照列,由于Cpno不是course的主码,因此如果删除或修改了Cno为1的元组,那么Cpno为1的单元格可以设置为空值。
对于参照完整性,除了应该定义外码,还应该定义外码列是否允许空值,一般情况下,需要显示说明参照完整性的违约处理示例:
用户定义的完整性
用户定义的完整性是:针对某一具体应用的数据必须满足的语义要求
关系数据库管理系统提供了定义和检验用户定义完整性的机制,不必由应用程序承担
属性上的约束条件
create table时定义属性上的约束条件
- 列值非空(not null)
- 列值唯一(unique)
- 检查列值是否满足一个条件表达式(check)
不允许取空值
因为实体完整性约束条件隐藏了不允许取空值的条件,所以not null可以不写
列值唯一
因为实体完整性约束条件隐藏了列值唯一的条件,所以可以不写unique
用check短语指定列值应该满足的条件
属性上的约束条件检查和违约处理
插入元组或修改属性的值时,关系数据库管理系统检查属性上的约束条件是否被满足,如果不满足则操作被拒绝执行
元组上的约束条件
元组上约束条件的定义
在create table时可以用check短语定义元组上的约束条件,即元组级限制
同属性值限制相比,元组级的限制可以设置不同属性之间的取值的相互约束条件
元组上约束条件检查和违约处理
插入元组或修改属性的值时,关系数据库管理系统检查元组上的约束条件是否被满足,如果不满足则操作被拒绝执行
完整性约束命名子句
完整性约束命名子句
constraint <完整性约束条件名><完整性约束条件>
<完整性约束条件>包括not null,unique,primary key短语,foreign key短语,check短语等
修改表中的完整性限制
使用alter table语句修改表中的完整性限制
域中的完整性限制(自学)
一般的,域是一组具有相同数据类型的值的集合,SQL支持域的概念,并可以用create domain语句建立一个域以及该域应该满足的完整性约束条件,然后就可以用域来定义属性。这样定义的有点是,数据库中不同的属性可以来自同一个域,当域上的完整性约束条件改变时,只要修改域的定义即可,而不必一一修改域上的各个属性,类似于在C语言中的全局变量的定义一样。
域的修改和删除方式与表的修改删除方式一致
断言
在SQL中可以使用数据定义语言中的create assertion语句,通过声明性断言来指定更具一般性的约束。可以定义涉及多个表或聚集操作的比较复杂的完整性约束。断言创建以后,任何对断言中所涉及关系的操作都会触发关系数据库管理系统对断言的检查,任何使断言不为真值的操作都会被拒绝执行。
创建断言的语句格式
create assertion <断言><check子句>
每个断言都被赋予一个名字,check子句中的约束条件与where子句的条件表达式类似
上述代码的意思是,每当我们对表进行插入操作的时候,断言asse_sc_db_num就会被触发检查,如果选修数据库课程的人数已经超过60人,则check子句返回'假' ,于是插入操作被拒绝执行。
删除断言的语句格式
drop assertion <断言名>
触发器
触发器是用户定义在关系表上的一类由事件驱动的特殊过程。一旦定义,触发器将被保存在数据库服务器中。任何用户对表的增删改操作均有服务器自动激活相应的触发器,在关系数据库管理系统核心层进行集中的完整性控制。
触发器类似于约束,但是比约束更加灵活,可以实施更为复杂的检查和操作,具有更加精细和强大的数据控制能力。
定义触发器
触发器又叫做事件—条件—动作规则。当特定的系统事件发生时,对规则的条件进行检查,如果条件成立则执行规则中的动作,否则不执行该动作。规则中的动作体可以很复杂,可以设计其他表和其他数据库对象,通常是一段SQL存储过程。
DBMS核心层集中的完整性控制SQL使用create trigger命令建立触发器,其一般格式为
create trigger<触发器名>
{before | after} <触发事件>on<表名> --指明触发器激活的时间是在执行触发事件前、后
referencing new | old row as <变量>
for each {row | statement} --定义触发器类型,指明动作体执行的频率
[where <触发条件>]<触发动作体> --仅当触发条件为真时才执行触发动作体
定义触发器的语法说明
- 表的拥有者才可以在表上创建触发器
- 触发器名
- 触发器名可以包含模式名,也可以不包含模式名
- 同一模式下,触发器名必须是唯一的
- 触发器名和表名必须在同一模式下
- 表名
- 触发器只能定义在基本表上,不能定义在视图上
- 当基本表的数据发生变化的时候,将激活定义在该表上相应触发事件的触发器
- 触发事件
- 触发事件可以使增删改或者这三个事件的组合
- 还可以update of<触发列,...>,即进一步指明修改那些列时激活触发器
- after/before是触发的时机
- after表示在触发事件的操作执行之后激活触发器
- before表示在触发事件的操作执行之前激活触发器
- 触发器类别
- 行级触发器(for each row)——有多少行触发多少次
- 语句级触发器(for each statement)——触发一次
- 触发条件
- 触发器被激活时,只有当触发条件为真时触发动作体才执行,否则不执行
- 如果省略when触发条件,则触发动作体在触发器激活后立即执行
- 触发动作体
- 出发动作体可以是一个匿名PL/SQL过程块,也可以是对已创建存储过程的调用
- 如果是行级触发器,用户都可以在过程体中使用new和old引用事件之后的新值和事件之前的旧值
- 如果是语句级触发器,则不能在触发动作体中使用new或old进行引用
- 如果触发动作体执行失败,激活触发器的时间就会终止执行,触发器的目标表或触发器可能影响的其他对象不发生任何变化
激活触发器
触发器的执行,是由触发事件激活的,并由数据库服务器自动执行
一个数据表上可能定义了多个触发器,遵循如下的执行顺序
- 执行该表上的before触发器
- 激活触发器的SQL语句
- 执行该表上的after触发器
删除触发器
删除触发器的SQL语法:
drop trigger <触发器名>on<表名>
触发器必须是一个已经创建的触发器,并且只能由具有相应权限的用户删除