[关闭]
@3013216027 2016-01-28T13:18:29.000000Z 字数 6141 阅读 1076

第7章 键和约束

数据库原理复习专栏


7.1 约束的类型

  1. create table studio (
  2. name varchar(30) primary key,
  3. address varchar(255),
  4. producer_id int references movie_exec(id) --制片人编号
  5. on delete set null
  6. on update cascade
  7. );

又如需要“更新和删除被引用者时外键都对应地更新和删除”,可以写:

  1. create table studio (
  2. name varchar(30) primary key,
  3. address varchar(255),
  4. producer_id int references movie_exec(id) --制片人编号
  5. on delete cascade --注意此处换成了cascade,删除时也“级联”地删除
  6. on update cascade
  7. );

习题 7.1.1

  1. alter table movies
  2. add foreign key (producerC#) references MovieExec(name)
  1. alter table Movies
  2. add foreign key (producerC#) references MovieExec(name)
  3. on update set null
  4. on delete set null
  1. alter table Movies
  2. add foreign key (producerC#) references MovieExec(name)
  3. on update cascade
  4. on delete cascade
  1. alter table StarsIn
  2. add foreign key (moveieTitle) references Movies(title)
  1. alter table StartsIn
  2. add foreign key (starName) references MovieStar(name)
  3. on delete cascade

update时应该稍加考虑?


7.2 属性和元组上的约束

  1. create table student (
  2. id int not null primary key,
  3. gender char(1) check (gender in ('F', 'M'))
  4. );

当然,上述等价于:

  1. create table student (
  2. id int not null primary key,
  3. gender enum('F', 'M')
  4. );
  1. create table movie_star (
  2. name varchar(30) primary key,
  3. address varchar(255),
  4. gender char(1),
  5. birthdate date,
  6. check (gender = 'F' or name not like 'Ms.%')
  7. );

check中可以添加的语句和where时一样的,同样可以使用诸如orlikenot等等来表达想要得到的约束条件。最后,事实上把check写在单个属性的后面来添加对该属性的check约束只是一个习惯而已,单个属性后同样可以做多属性的check约束--唯一的区别就是此时无法使用尚未声明的属性。最后,几乎所有数据引擎(data engine,如InnoDB)都会解析check但并不会产生任何动作,合理的替代方案是使用Before触发器。

习题 7.2.3

  1. create table StarsIn (
  2. -- ...
  3. check ((select year(birthdate) from MovieStar where name = starName) <= (select year from Movies where title = movieTitle))
  4. );
  1. create table Studio (
  2. -- ...
  3. unique key (address)
  4. -- 参考答案:CHECK (address IS UNIQUE)...
  5. );
  1. create table MovieStar (
  2. -- ...
  3. check (name not in (select name from MovieExec))
  4. );
  1. create table Studio (
  2. -- ...
  3. check (name in (select studioName from Movies))
  4. );
  1. create table Movies (
  2. -- ...
  3. check (
  4. producer not in (select presC# from studio)
  5. or studioName in (select name from Studio where pres = producer)
  6. )
  7. );

7.3 修改约束

 习题 7.3.1


7.4 断言

  1. #include <iostream>
  2. #include <cassert>
  3. int main() {
  4. int x;
  5. while (std::cin >> x) {
  6. assert (x != 0);
  7. std::cout << 1.0 / x << std::endl;
  8. }
  9. return 0;
  10. }

程序功能是不断读入一个整数,并输出其倒数。注意上述assert,我们断定括号中的必须为真,即必须不能为,否则直接中断,下该程序进程会被发送一个(),即核心转储并终止进程(),上述在程序调试时非常地有用。回到数据库中,这个断言也是差不多的,唯一的区别就是在数据库中断言失败后中断的不是程序进程,而是诸如过程()、函数()以及数据库操作(增删改)。

  1. CREATE ASSERTION RichPres
  2. CHECK(NOT EXISTS
  3. (SELECT Studio.name FROM Studio, MovieExec
  4. WHERE pres = cert AND netWorth < 10000000
  5. )
  6. );
约束类型 声明位置 (检查)动作发生的时机 确保成立?
基于属性的CHECK 属性 对关系插入元组或属性修改时 如果是子查询,则不能确保
基于元组的CHECK 关系模式元素 对关系插入元组或属性修改时 如果是子查询,则不能确保
断言 数据库模式元素 对任何提及的关系做改变时

说白了,前两者是放在某个具体的表中的,因此仅在表数据修改时(更新UPDATE,添加INSERT,删除DELETE)检查,如果做的是查询(SELECT)则不影响表中数据,自然不会去检查;断言是放在某个数据库中且不存放于具体的某个表中的,因此balabala......


7.5 触发器

  1. CREATE TRIGGER NetWorthTrigger -- 触发器名称为NetWorthTrigger
  2. AFTER UPDATE OF netWorth -- AFTER指定触发器在事件发生后触发,UPDATE指定事件为“发生记录的更新”,OF指定列,合起来就是“在列netWorth被更新后触发这个触发器”
  3. ON MovieExec -- 在哪个表上添加这个触发器
  4. REFERENCING -- 下面做一些别名(alias)设置
  5. OLD ROW AS OldTuple,
  6. NEW ROW AS NewTuple
  7. FOR EACH ROW -- FOR EACH ROW指定对每一行,执行下面的语句
  8. WHEN (OldTuple.netWorth > NewTuple.netWorth)
  9. UPDATE MovieExec
  10. SET netWorth = OldTuple.netWorth
  11. WHERE cert = NewTuple.cert;

注:不能完整地支持上述的特性,其新记录直接使用名称,旧记录直接为,在需要指定属性(即指定列)时可以在触发器的部分使用如if (new.name <> old.name) then ...来替代解决。

  1. create trigger fix_year
  2. before insert on movies
  3. referencing
  4. new row as newrow
  5. old row as oldrow
  6. for each row
  7. when newrow.year is null
  8. update newstuff set year = 1995;
  1. create trigger avgtrigger
  2. after update of netWorth on MovieExec
  3. referencing
  4. old table as oldstuff
  5. new table as newstuff
  6. for each statement
  7. when (60000 > (select avg(netWorth) from MovieExec))
  8. begin
  9. delete from MovieExec
  10. where (name, address, cert, netWorth) in newstuff;
  11. insert into MovieExec (SELECT * FROM oldstuff);
  12. end;

功能是如果更新记录后新的平均值过低(小于60000),则恢复之前的数据(删掉新加的数据,并添加进原有数据)。

习题 7.5.1

  1. create trigger avg_delete_trigger
  2. after delete on MovieExec
  3. referencing
  4. old table as oldstuff
  5. for each statement
  6. when (60000 > (select avg(netWorth) from MovieExec))
  7. begin
  8. insert into MovieExec (select * from oldstuff);
  9. end; -- 删除后平均值过低则恢复被删除数据
  10. create trigger avg_insert_trigger
  11. after insert on MovieExec
  12. referencing
  13. new table as newstuff
  14. for each statement
  15. when (60000 > (select avg(netWorth) from MovieExec))
  16. begin
  17. delete from MovieExec where (name, address, cert, netWorth) in newstuff;
  18. end; -- 添加后平均值过低则删除添加进的数据
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注