首页 理论教育分布式数据库技术的集中式语义完整性控制

分布式数据库技术的集中式语义完整性控制

【摘要】:语义完整性子系统有两种主要成分:一种表达与操纵完整性断语的语言,一种在数据库更新时强制数据库实施特定动作的强制机制。前者称为完整性约束,后者称为触发器。3)域完整性在关系中,每个属性有指定的域,并且该域的值会有限制。4)参考完整性参考完整性是指关系的外键的值必须与其参考关系中的主键相匹配。在关系型数据库系统中,可以使用断语来定义完整性约束。

语义完整性子系统有两种主要成分:一种表达与操纵完整性断语的语言,一种在数据库更新时强制数据库实施特定动作的强制机制。前者称为完整性约束(integrity constraints),后者称为触发器(trigger)。

1.完整性约束

完整性约束涵盖的内容甚多,主要包括零值完整性(null integrity)、实体完整性(entity integrity)、域完整性(domain integrity)、参考完整性(referential integrity)。

1)零值完整性

零值是指该数据值暂时未知,例如,在一个人事关系中,某人的年龄可能未知,因此在获得确切值前可让其取零值。

2)实体完整性

实体完整性意指关系的主键不能取零(空)值。

3)域完整性

在关系中,每个属性有指定的域,并且该域的值会有限制。例如,人的年龄不能为负值,即年龄不能小于零。

4)参考完整性

参考完整性是指关系的外键的值必须与其参考关系中的主键相匹配。值得一提的是,外键的值也可以是零值。

完整性约束可以由数据库管理员使用高级语言来操纵。下面使用的是一种说明性语言,也就是伪语言,风格与SQL语言兼容。

在关系型数据库系统中,可以使用断语来定义完整性约束。断语是关系演算的一种特定表达式,可以使用全称量词,也可以使用存在量词。

我们可以指定预定义的、预编译的和通用的三种不同的完整性约束。

为了便于说明,下面给出一种数据库关系模式:

Student(#sno,sname,age,dno,type)

COURSE(#cno,cname,level,durat ion,c redi t_rate)

SC(#sno,#cno,score)

这里涉及三个关系:Student是一个学生关系,其属性为学号(sno)、姓名(sname)、年龄(age)、系别(dno)和类别(type);COURSE描述的是课程,属性为课程编号(cno)、课程名(cname)、等级(level)、课时(duration)和学分(credit_rate);而SC则把这两个关系关联起来,说明谁(sno)修哪门课(cno),分数是多少(score)。

一种约束称为预定义约束。预定义约束是基于简单关键词的,通过它们,可以准确地表达更通用的约束,如非NULL属性、唯一键、外键或函数依赖性等。例如,关系Student中的学号不能为空(NULL),则它可以表示为:

sno NOT NULL IN Student

【例4.4】 唯一键(unique key)问题。令偶对(sno,cno)是关系SC的唯一键,则可以表示为:

(sno,cno)UNIQUE IN SC

【例4.5】 外键(foreign key)问题。令关系SC中的课程号cno是一个外键,因为它与关系COURSE中的主键cno对应,即如果SC中出现一个cno,则它必须在关系COURSE中存在,可以表示为:

cno IN SC REFERENCES cno IN COURSE

【例4.6】 函数依赖性(functional dependency)。学生关系中,学生的学号函数性地决定了学生姓名,可以表示为:

sno IN Student DETERMINES sname

完整性约束可以在编译阶段就检查,预编译以后的约束表示关系的所有元组在给定更新类时必须满足前置条件。这里讲的更新类别是INSERT、DELETE或MODIFY,可以对它们实施限制完整性控制。为了在约束定义中便于辨识,可使用两个变量NEW和OLD来说明更新的元组状态。预编译约束可以用CHECK语句来表示,其语法如下:

CHECK ON<关系名>WHEN<更新类别>(<限定说明>)

下面列举预编译约束的例子。

【例4.7】 域约束(domain constraint)问题,例如,人的年龄只能是1到150岁,可以表示为:

CHECK ON PERSON(age>0 AND age<=150)

【例4.8】 删除时的域约束在应用中常常出现,例如,银行可以把一直睡眠的账号(ACCOUNT)将其年费扣除到账面为0后再将该账号自动撤销,并在项目预算为0时删除该元组。后者可以记作:

CHECK ON ACCOUNT WHEN DELETE(ba lance=0)

【例4.9】 转换限制,如人的年龄只能增加不能减少,可以表示为:

CHECK ON PERSON(NEW.age>OLD.age AND NEW.pno=OLD.pno)

也可以使用元组关系演算公式来表达一般约束,其中所有变量都是有限定的。数据库系统必须保证这些公式始终为“真”。

值得一提的是,通用约束(general constraints)比预编译约束更简洁,因为前者涉及多个关系。

通用约束可以用如下形式表示:

CHECK ON l is t of<变量名>:<关系名>,(<限定>)

【例4.10】 描述函数依赖性,则例4.6的约束可以表达为:

CHECK ON s1:Student,s2:Student

(s1.sname=s2.sname IF s1.sno=s2.sno)

【例4.11】 聚集函数的限制,例如,外国留学生学习签证的累计逗留年限不超过8年,可以表达为:

CHECK ON s:Student:v:Visa(SUM(v.dur WHERE s.sname=v.pname))≤8 i f s.Nat ional i ty≠"CHINA"

2.触发器

触发器扮演着强制数据完整性的角色。一般来说,它对数据实施一个例程(routine),以保证数据的完整性。值得一提的是,这类例程是在SQL运算时隐式自动触发的。

使用触发器也需要授权,相应的权限如下。

●创建触发器。

●为了创建触发器,用户必须拥有修改该表格的权限或ALTER ANY TABLE权限。

●为了修改触发器,用户必须拥有该触发器及ALTER ANY TRIGGER权限。因为触发器作用在某个关系上,所以用户必须拥有该表格的修改权限或ALTER ANY TABLE权限。

●如果要在数据库级别创建触发器,用户必须拥有ADMINISTER DATABASE TRIGGER系统级权限。

创建触发器的语法如下:

Create[ORREPLACE]TRIGGER<t r igger name>[BEFORE/AFTER/INSTEADOF][INSERT/UPDATE/DELETE[of column,...]]ON<table name>[REFERENCING[OLD[AS]<old name>|NEW[AS]<new name>][FOR EACH STATEMENT/FOREACHROW][when<condi t ion>][BEGIN-PL/SQL b l ock END];

基本上,触发器包括触发事件或语句、触发器限制、触发器动作三个部分。

1)触发事件或语句

触发事件或语句可以是一条SQL语句、一个数据库事件或用户事件(update、delete、insert等),它们会导致触发器触发。触发器语句或事件可以为:对特定表或视图的INSERT、UPDATE或DELETE,任意模式对象的CREATE、ALTER或DROP,数据库的启动或关闭,用户的登录与退出,某些出错消息。

2)触发器限制(www.chuimin.cn)

触发器限制是一个逻辑表达式,其结果可以是TRUE/FALSE/UNKNOWN,即真/假/未知。要触发一个触发器,则逻辑表达式必须为真(TRUE)。

3)触发器动作

触发器动作是一个SQL语句块,包含SQL语句和限制为真时要执行的代码。

下面选用参考文献[8]中的一个例子加以说明:

Create TRIGGER NewWor thTr igger

AFTERUpdate NewWor th ON MovieExec

REFERENCING

OLDAS Ol dTup le

NEWAS NewTup le

when(Ol dTuple.NewWor th>NewTup le.NewWor th)

Update MovieExec

SET NewWor th=OldTup le.NewWor th

where cer t#=NewTup l e.cer t#

FOREACHROW

这个例子创建了一个触发器,取名为New Worth Trigger,其在关系MovieExec更新时生效(使用AFTER Update New Worth ON MovieExec表述),而且对每个元组有效(使用FOR EACH ROW)。

下面我们先对完整性约束作一番深入讨论。

3.强制完整性

可以通过强加的语义完整性约束拒绝那些会损害完整性约束的更新程序。如果由于更新而产生的数据库新状态不合理,我们就说它破坏了约束。设计一个完整性子系统时的主要困难是如何找到有效的强制算法

有两种基本方法允许拒绝不一致的更新。

方法1:不一致检测。

执行一个更新u时,会产生一个从状态D到状态D′(即D→D′)的数据库状态改变。强制算法通过测试来验证相关的约束在D′状态时是否依然满足。如果状态D′是不一致的,则DBMS可以利用补偿动作将之改变到另外一个状态D″,或者执行Undo(u)将之恢复到原来的D状态。所谓的补偿动作,如某账号上要转入500元,转出账号扣款成功,而转入未成功,引起了账面不平衡,数据库就处于不一致状态。可以向转出账号(即这500元的源账号)转回500元来作为补偿。

因为这种测试是在更新后实施的,故称为后测试。后测试采用的是做了再说的策略。如果更新D时要进行大量的Undo或补偿的话,这种方法的效率就很低。

方法2:基于不一致预防。

更新仅当它能把数据库转换成一致状态时才可以被实施。涉及更新的元组可以是直接指定的,也可以是从数据库中检索出来的结果。实施前应进行强制算法验证,以确定更新实施后那些涉及的元组是否依然保持相关的约束。

因为这种测试是在更新实施前进行的测试,故称为前测试。由于避免了Undo,所以预防方法比检测方法更高效。

【例4.12】 查询:新年后将每个人的年龄加一岁,可以表述如下:

Update Student

SETAGE=AGE+1

强加上约束,它会转换成如下查询:

Update Student

SET AGE=AGE+1

AND NEW.AGE>0

AND NEW.AGE<=150

由上可见,实施修改查询算法很方便:运行时通过将断语谓词和更新谓词实施逻辑“与”运算后进行预测试,以确定其是否依然成立。

可以使用元组演算来描述。

【例4.13】 例4.7中的外键断语可以记作:

g∈SC,∀j∈COURSE:g.cno=j.cno

为了处理更一般的断语,可以在定义阶段实施预测试,然后在更新发生的时间内将之强制推广。

下面描述预防性的方法。令u为关系R上的一个更新,R+和R-是指该更新后的关系变化,R+是u插入关系R的元组集,R-是u删除关系R中的元组集。如果u是一个插入操作,则R-是空集。如果u是一个删除操作,则R+是空集。如果u是一个修改操作,则修改后的结果是R+∪(R-R-)。

编译后的断语是一个三元组(R,U,C),其中,R为一个关系,U是一个更新类,C是一个断语。

具体来说,如果定义一个完整性约束I,则可以对I涉及的关系产生一个编译后的断语集。I中涉及的关系被u(u∈U)更新时,必须检验编译后的断语,以确定是否满足I强制的约束。

可以在原始断语上运用变化规则获得编译后的断语,这些规则是以断语量词的语法分析为基础的。它们允许在基关系上实施关系替代,因为编译后的断语比原始断语更简单,所以可以把这个过程称为简化。

【例4.14】 考虑例4.13中的外键约束被修改后的表达式。编译后的断语及约束如下:

(SC,INSERT,C1),(COURSE,DELETE,C2)和(COURSE,MODIFY,C3

其中:C1

∀NEW∈SC+,∃j∈COURSE:NEW.cno=j.cno C2

∀g∈SC,∀OLD∈COURSE-:g.cno≠OLD.cno C3

∀g∈SC,∀OLD∈COURSE-,∃NEW∈COURSE+:g.cno≠OLD.cno OR OLD.cno=NEW.cno

这三个约束都是关于参考完整性的,它们的含义可以简述如下。

C1:表示若在SC中插入一条记录,则记录中的课程号(cno)必须已在COURSE中定义。

C2:表示若删除COURSE中的一条记录,则必须保证SC中没有与该记录的cno一样的课程记录,以避免SC中的记录出现“孤子”。

C3:表示若要修改COURSE中的记录,当修改涉及cno时,则要求SC中不存在涉及改动前与cno相关的记录(否则又变成“孤子”)(即g.cno!=OLD.cno),或者修改不涉及cno属性(即OLD.cno=NEW.cno)。

强制算法是让一个更新程序的更新关系R的所有元组满足某种限定条件。该算法分两步操作:第一步从R产生差异关系R+和R-。第二步负责检索R+和R-中的元组,并找出其中不满足编译后约束的元组。如果没有找出不满足编译后约束的元组,则说明约束的元组是满足的。

【例4.15】 假设关系COURSE上有一个删除操作,强制的(COURSE,DELETE,C2)包括如下语句:

result←retrieve all tuples of COURSE-where(C2

这样,如果结果为空,则更新验证满足断语。

为了保证数据库的完整性,除了断语,还有一个功能是触发器(trigger)。触发器可看成是存储过程,当针对表执行特定的动作时,就会激活它。当针对表进行插入、更新、删除或三种操作的结合时,激活触发器,也可以在某行被影响或某条语句出现时被激活。触发器常用于加强数据完整性约束和业务规则中。