,再创建一个Student表,它的属性D#上声明了一个引用Dept(D#)的外键:
create table Student( S# char(10) primary key, name char(20), gender char(1) check (gender in ('F', 'M')), D# char(3) foreign key references Dept(D#) );
上面的声明等价于:
create table Student( S# char(10) primary key, name char(20), gender char(1) check (gender in ('F', 'M')), D# char(3), contraint dfk foreign key references Dept(D#) );
这里外键的约束就是限制学生表里的D#必须在系表里的D#中出现,那么很容易想到一个问题:如果USTC新任校长万老大把11系拆了,也就是系表里把D#为’011’的元组删除了的话,那我们11系的学生在Student表里的tuples不就不满足外键约束了吗
这时候就需要引入关联动作,关联动作分为级联(Cascade)动作和设置(Set)动作
1.级联动作
级联动作就是一系列的主键-外键-主键-外键-…约束引用链条的后端的某个主键delete或update,会产生(自动的,触发器)沿引用链条从后往前的一系列外键的delete或update操作
例如我们声明一个D#的删除级联,即:
create table Student( S# char(10) primary key, name char(20), gender char(1) check (gender in ('F', 'M')), D# char(3) foreign key references Dept(D#) on delete cascade );
此时删除11系,那么根据级联动作定义,所有11系的学生都被删除,因为Student表通过外键引用了Dept表的主键
再假设Dept表引用了一个学校编号U#的外键,它是一个University表的主键,此时构成了一个引用链条,若这个University表删除了USTC的条目,那么根据级联动作定义USTC的11系,11系的学生都将被自动删除
级联动作也可以是on update cascade,比如11系改系别号为1系了,那么11系的学生能够自动的把D#改成‘001’
DBMS处理级联动作是按事务来处理的,若在引用链条中某一个部分的级联动作违反了其他的完整性约束,那么整个修改动作都会被回滚(事务的概念),即修改失败
ppt中写道:
Referential integrity is only checked at the end of a transaction
–– Intermediate steps are allowed to violate referential integrity provided later steps remove the violation, otherwise it would be impossible to create some database states, e.g. insert two tuples whose foreign keys point to each other
很不幸的是在实验中我发现这条规则MySQL适用但Oracle并不适用(至少在PL/SQL中)
SQL支持一个外键同时有一个删除级联和一个更新级联
2.设置动作
设置动作比较好理解:
on delete set null on delete set default
5.4 断言和触发器
(1)断言
SQL还支持使用断言(Assertion)在关系外部定义数据库必须满足的条件,语法为:
create assertion
check
;
Assertion有点类似于C.J.Date定义的数据库约束,可以针对单表或多表
ppt中一个复杂的例子,限定每个部门的工资总和必须小于d1部门的工资总和:
create assertion sac check( not exists( select * from EMP e2 where (select sum(sal) from EMP e1 where e1.d# = e2.d#) >= (select sum(sal) from EMP where d# = 'd1') ) );
PS:我咋觉得第一个where后面要加d# ~= 'd1' and呢?
(2)触发器
触发器定义了数据库状态改变(元组修改)时需要自动执行的一系列动作,定义触发器包括两个要点:
触发条件 执行动作
触发器还有几个重要属性:
事前触发或事后触发 行触发还是整体触发
事前触发或事后触发比较好理解,行触发还是整体触发是指的是一条update或delete时若涉及多个元组的修改,是整体触发一次还是每个元组都触发一次
还是举例说明,下面是我在Oracle上创建的行触发器:
create or replace trigger countStud after delete or insert or update on Stud for each row begin update Dept set S_count = S_count+1 where D# = :new.D#; update Dept set S_count = S_count-1 where D# = :old.D#; end;
这是一个用来统计系里人数的触发器(或者说叫做保证系里人数完整性的触发器),分点说明:
after delete or insert or update on Stud指定了触发器的条件和触发时机:当Stud表有删除、插入或更新操作之后触发 for each row声明这是一个行触发器,按行触发 begin和end包体里指定了触发器的动作
至于其中的:new和是:old保留变量,对应删除、插入或更新三个操作他们有如下含义:
?
含义 |
insert |
update |
delete |
:new |
新插入的元组 |
更新后的元组 |
null |
:old |
null |
更新前的元组 |
删除的元组 |