数据库知识整理 - 数据库完整性

版权声明: https://blog.csdn.net/Ha1f_Awake/article/details/84344606

主要内容

1. 属性上的约束条件

2. 元组上的约束条件

1. 定义触发器

2. 触发器激活顺序


 

数据的完整性和安全性是两个既有联系又不尽相同的概念。数据的完整性是为了防止数据库中存在不符合语义的数据;数据的安全性是保护数据库免受恶意破坏和非法存取。

下面我们将进一步了解数据库的完整性。


 

实体完整性

关系模型的实体完整性是关于元组(记录)唯一性的定义,在CREATE TABLE 中用PRIMARY KEY 来实现。

主码由单个属性构成时,可使用列级约束条件表级约束条件;而当主码由多个属性构成时,只能使用表级约束条件。

关于约束条件的内容可以回顾“基本表的定义、删除和修改”

 

PROMARY KEY 短语定义了关系的主码后,每当用户程序对基本表插入一条记录或对主码列进行更新操作时,关系数据库管理系统都会按照实体完整性进行全表扫描,依次判断表中的每一条记录的主码值是否与即将插入记录的主码值相同。

但这种全表扫描效率极低,因而关系数据库管理系统一般会在主码上自动建立一个索引


 

小拓展:索引的应用

假设在Student表中,学生的姓名Sname为主码。

在基本表中,学生的记录是无序地逐条插入的,现在我们先后插入了Tom、Amy、Jenny、Belly、Jack和Anne的记录。

(1)如果我们要查询Anne的资料,首先得找到“Anne”所在的元组,然后才能输出她的资料,而这样我们必须得遍历6个元组,因为Anne的记录在最后一行元组。

(2)如果我们要查询Alex的资料,显而易见,Alex并不在Student表中,但计算机是不知道的,它必须得一个个去查去对比。遍历6个元组后,它才终于得出结论——No result。

(3)如果我们要插入新同学Ben的资料,计算机依然要遍历6个元组,确定无重复后才能插入记录。

 

当我们使用索引后,Student表中的记录顺序就会变成Amy、Anne、Belly、Jack、Jenny和Tom,按照字母表的顺序。

接着我们再重复一遍上面的操作:

(1)查询Anne的资料:根据索引,“A”有Amy和Anny,“A-n”只有Anne,可以开始查询,一共遍历2个元组

(2)查询Alex的资料:根据索引,“A”有Amy和Anne,“A-l”没有对应的元组,无法查询,一共遍历2个元组

(3)插入Ben的资料:根据索引,“B”只有Belly,“B-e”也还是Belly,而“B-e-n”无重复值,可以插入,一共遍历1个元组

 

从这个简单的例子我们就可以看出索引对于查询效率的重大意义,以后有时间我会围绕“索引”再写一篇博文。


 

参照完整性

关系模型的参照完整性在CREATE TABLE 中用FOREIGN KEY 短语定义哪些列为外码,用REFERENCES 短语指明这些外码参照哪些表的主码。

关于约束条件的内容可以回顾“基本表的定义、删除和修改”

 

参照完整性将两个表中的相应元组联系起来了。因此,对参照表或被参照表进行增、删、改操作时,都有可能会破坏参照完整性,因此我们必须对违约操作进行处理:

(1)拒绝执行(NO ACTION)—— 默认策略;

(2)级联操作(CASCADE):当删除或修改被参照表的一个元组导致与参照表不一致时,同时删除或修改参照表中所有相关的元组;

(3)设置为空值(SET NULL):当删除或修改被参照表的一个元组导致与参照表不一致时,将参照表中所有相关的元组的对应属性值设为空值。

 

//显式说明参照完整性的违约处理示例:

CREATE TABLE SC

(Sno CHAR(9),

Cno CHAR(4),

Grade SMALLINT,

PRIMARY KEY(Sno,Cno),                                       /*定义表级实体完整性*/

FOREIGN KEY(Sno) REFERENCES Student(Sno)

           ON DELETE CASCADE

           ON UPDATE CASCADE,

FOREIGN KEY(Cno) REFERENCES Course(Cno)

           ON DELETE NO ACTION                              /*当删除Course表中的元组造成与SC表不一致时,拒绝删除*/

           ON UPDATE CASCADE                                /*当更新Course表中的元组造成与SC表不一致时,级联更新*/

);


 

用户定义的完整性

 

1. 属性上的约束条件

在CREATE TABLE 中定义属性的同时定义约束条件:

(1)列值非空(NOT NULL);

(2)列值唯一(UNIQUE);

(3)检查列值是否满足一个条件表达式(CHECK短语)。

关于约束条件的内容可以回顾“基本表的定义、删除和修改”

不满足约束条件的操作将被拒绝执行。

 

//Student表的Ssex只允许取“男”或“女”:

CREATE TABLE Student(

Sno CHAR(9) PRIMARY KEY,

Sname CHAR(20) UNIQUE,            

Ssex CHAR(2) NOT NULL CHECK(Ssex IN ('男','女')),

Sage SMALLINT,

Sdept VARCHAR(20)

);

 

2. 元组上的约束条件

同属性值限制相比,元组级的限制可以设置不同属性之间取值的相互约束条件。

//当学生性别为男生时,其名字不能以“Ms.”开头:

CREATE TABLE Student(

Sno CHAR(9) PRIMARY KEY,

Sname CHAR(20) UNIQUE,            

Ssex CHAR(2) NOT NULL CHECK(Ssex IN ('男','女')),

Sage SMALLINT,

Sdept VARCHAR(20),

CHECK (Ssex = '女' OR Sname NOT LIKE 'Ms.%')

);


 

完整性约束命名子句

通俗点讲就是给一个约束条件起名字,方便增、删和改。需要先提一下的是,对于约束条件并没有ALTER短语,我们只能先把旧的删掉再插入一个新的。

//学号在20170000~20170499之间,姓名不能取空值,年龄小于30,性别只能是“男”或“女”:

CREATE TABLE Student(

Sno NUMERIC(9)

      CONSTRAINT C1 CHECK (Sno BETWEEN 20170000 AND 20170499),

Sname CHAR(20)

      CONSTRAINT C2 NOT NULL,

Ssex CHAR(2)

      CONSTRAINT C3 CHECK(Ssex IN ('男','女')),

Sage NUMERIC(2),

      CONSTRAINT C4 CHECK(Sage < 30),

CONSTRAINT StudentPKey PRIMARY KEY(Sno) 

);

 

//去掉对性别的限制:

ALTER TABLE Student

           DROP CONSTRAINT C3;

 

//增加新的约束条件:

ALTER TABLE Student

           ADD CONSTRAINT C5 CHECK(Sage < 25);


 

域中的完整性约束条件

域是一组具有相同数据类型的值的集合,类似于枚举类型。

//建立一个性别域并声明域的取值范围:

CREATE DOMAIN GenderDomain CHAR(2)

         CONSTRAINT GD CHECK (VALUE IN ('男','女'));


 

断言

 

在SQL中可以用CREATE ASSERTION 语句来指定更具一般性的约束。

断言创建以后,任何对断言中所涉及关系的操作都会触发关系数据库管理系统对断言的检查,任何使断言不为真值的操作都会被拒绝执行。

 

//限制数据库课程最多60名学生选修:

CREATE ASSERTION ASSE_SC_NUM

CHECK (60 >= (

          SELECT COUNT(*)

          FROM Course,SC

          WHERE SC.Cno = Course.Cno AND Course.Cname = '数据库')

              );

每当学生选修课程时,将在SC表中插入一条元组(Sno,Cno,NULL),ASSE_SC_NUM断言被触发检查。如果选修数据库课程的人数已经超过60人,CHECK子句返回值为“假”,对SC表的插入操作会被拒绝。

 

//删除对数据库课程的断言:

DROP ASSERTION ASSE_SC_NUM;


 

触发器

 

触发器(Trigger)是用户定义在基本表上的一类由触发事件驱动的操作过程。

触发器被定义后会保存在数据库服务器中。任何用户对表的增、删、改操作均由服务器自动激活相应的触发器,在关系数据库管理系统核心层进行集中的完整性控制。

触发器类似于约束,但比约束更加灵活。

 

需要注意的是,触发器在SQL99后才写入SQL标准,但是很多关系数据库管理系统在统一标准之前就产生了各自的触发器,因而这些触发器的语法各不相同、互不兼容。上机实验时需要留意所用系统的触发器说明。

 

1. 定义触发器

触发器又叫“事件-条件-动作”规则(event-condition-action)。当特定的触发事件发生时,对规则的条件进行检查,若条件成立,规则中的动作将被执行,反之拒绝执行。若不设置规则的动作条件,触发事件发生时立即执行动作。

一般格式:

CREATE TRIGGER <触发器名>

{BEFORE|AFTER} <触发事件> ON <目标基本表名>

REFERENCING {NEW|OLD} {ROW|TABLE} AS <临时变量名>

FOR EACH {ROW|STATEMENT}

[WHEN <触发条件>]

<触发动作体>

 

我们可以看到,在触发器的一般格式中有多个的可选项,下面会一个个介绍:

(1)CREATE TRIGGER <触发器名>

只有建立基本表的用户才有权限在表上创建触发器,并且一个表上只能创建一定数量的触发器,具体数量由关系数据库管理系统决定;

同一模式(命名空间)中,触发器名必须唯一,且必须与目标基本表位于同一模式。

 

(2){BEFORE|AFTER} <触发事件> ON <目标基本表名>

{BEFORE|AFTER} 指示触发器被激活的时机,在触发事件执行之前或之后;

触发事件可以是INSERTDELETEUPDATE,也可以是这几个事件的组合,还可以是UPDATE OF <触发列, ...>,即进一步制定修改哪些属性列时激活触发器;

ON <目标基本表名> 的意思就很明显了。

 

(3)REFERENCING {NEW|OLD} {ROW|TABLE} AS <临时变量名>

触发事件执行前后,元组或表的内容保存在<临时变量名>中,以便在触发动作体中使用。

需要注意的是,只有行级触发器才能在触发动作体中使用NEW|OLD变量,而在语级触发器中禁止使用。

/*现在看不懂上面几个新名词不要紧,下面会进一步说明*/

 

(4)FOR EACH {ROW|STATEMENT}

该语句可将触发器定义为行级触发器(ROW语级触发器(STATEMENT

当触发事件仅涉及基本表中的一行元组或元组中的一个属性值,我们可以将触发器定义为行级触发器。每当一个元组被UPDATE(或其他操作),且触发条件成立时,触发动作体就会执行一次,即一个个触发,一个个操作;

当触发事件涉及整体基本表的改动时,如插入或删除一行元组,则将触发器定义为语级触发器。所有INSERTDELETE语句执行后,且触发条件成立时,触发动作体才执行一次,即所有触发后,来一次总操作。

 

(5)[WHEN <触发条件>]

触发条件为可选语句,设置后在执行触发动作体前将进行条件判断,不设置则直接执行。

 

(6)<触发动作体>

触发动作体可以是INSERTDELETEUPDATEPL/SQL过程块

 

下面通过三个例子体会一下~

//当对表SC的Grade属性进行修改时,若分数增加了10%,则将此次操作记录到另一个表SC_U(Sno,Cno,Oldgrade,Newgrade)中:

CREATE TRIGGER SC_T

AFTER UPDATE OF Grade ON SC

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)

 

//将每次对表Student的插入操作所增加的学生个数记录到C中:

CREATE TRIGGER Student_Count

AFTER INSERT ON Student

REFERENCING 

          NEW TABLE AS OldTable

FOR EACH STATEMENT                                /*定义为语级触发器*/

         INSERT INTO OldTable(Numbers)

         SELECT COUNT(*) FROM OldTable

(可回顾“插入数据”

 

//定义一个BEFORE行级触发器,为Teacher表定义完整性规则“教授的工资不得低于4000元,如果低于4000元,自动补为4000”:

CREATE TRIGGER Insert_OR_Update_Sal

BEFORE INSERT OR UPDATE ON Teacher

REFERENCING

          NEW ROW AS NewTuple

FOR EACH ROW

BEGIN                                              /*PL/SQL过程块开端*/

           IF (NewTuple.Job = '教授') AND (NewTuple.Sal < 4000)

                THEN NewTuple.Sal = 4000;

           END IF;                                 /*IF语句结束*/

END;                                                /*PL/SQL过程块结尾*/

 

//删除触发器:

DROP TRIGGER Insert_OR_Update_Sal ON Teacher;

 

2. 触发器激活顺序

先执行BEFORE触发器再执行AFTER触发器。同种触发器中,遵循“先创建先执行”的原则。有些关系数据库管理系统会按照触发器名称的字母排列顺序执行。


 

阅读更多

没有更多推荐了,返回首页