@3013216027
2016-01-28T05:18:29.000000Z
字数 6141
阅读 1173
数据库原理复习专栏
references <tablename> (<attr>)foreign key (<attr>) references <tabname> (<attr>)
create table studio (name varchar(30) primary key,address varchar(255),producer_id int references movie_exec(id) --制片人编号on delete set nullon update cascade);
又如需要“更新和删除被引用者时外键都对应地更新和删除”,可以写:
create table studio (name varchar(30) primary key,address varchar(255),producer_id int references movie_exec(id) --制片人编号on delete cascade --注意此处换成了cascade,删除时也“级联”地删除on update cascade);
alter table moviesadd foreign key (producerC#) references MovieExec(name)
alter table Moviesadd foreign key (producerC#) references MovieExec(name)on update set nullon delete set null
alter table Moviesadd foreign key (producerC#) references MovieExec(name)on update cascadeon delete cascade
alter table StarsInadd foreign key (moveieTitle) references Movies(title)
alter table StartsInadd foreign key (starName) references MovieStar(name)on delete cascade
update时应该稍加考虑?
NOT NULL来声明
create table student (id int not null primary key,gender char(1) check (gender in ('F', 'M')));
当然,上述等价于:
create table student (id int not null primary key,gender enum('F', 'M'));
CHECK扩展,即可得到基于元组的约束,如:
create table movie_star (name varchar(30) primary key,address varchar(255),gender char(1),birthdate date,check (gender = 'F' or name not like 'Ms.%'));
check中可以添加的语句和where时一样的,同样可以使用诸如or,like,not等等来表达想要得到的约束条件。最后,事实上把check写在单个属性的后面来添加对该属性的check约束只是一个习惯而已,单个属性后同样可以做多属性的check约束--唯一的区别就是此时无法使用尚未声明的属性。最后,几乎所有数据引擎(data engine,如InnoDB)都会解析check但并不会产生任何动作,合理的替代方案是使用Before触发器。
create table StarsIn (-- ...check ((select year(birthdate) from MovieStar where name = starName) <= (select year from Movies where title = movieTitle)));
create table Studio (-- ...unique key (address)-- 参考答案:CHECK (address IS UNIQUE)...);
create table MovieStar (-- ...check (name not in (select name from MovieExec)));
create table Studio (-- ...check (name in (select studioName from Movies)));
create table Movies (-- ...check (producer not in (select presC# from studio)or studioName in (select name from Studio where pres = producer)));
CONSTRAINT <约束名>即可alter table <表名> drop constraint <约束名>。注:mysql56需要将constraint换成primary key或者foreign key。如alter table test drop foreign key fk_test_student;。alter table <表名> add constraint <约束名> <约束类型> <约束内容>。如alter table test add constraint fk_test_student foreign key (id) references student(id)。alter table Movie add constraint pkMovie primary key(title, year);alter table Movie add constraint fkMovieMovieExec foreign key (producer) references MovieExec(cert);alter table Movie add constraint ckMovieLength check (length>=60 and length <= 250);alter table StarsIn add constraint ckStarName check (starName not in (select name from MovieExec));alter table Studio add constraint ckAddress check (address is unique);
#include <iostream>#include <cassert>int main() {int x;while (std::cin >> x) {assert (x != 0);std::cout << 1.0 / x << std::endl;}return 0;}
程序功能是不断读入一个整数,并输出其倒数。注意上述assert,我们断定括号中的必须为真,即必须不能为,否则直接中断,下该程序进程会被发送一个(为),即核心转储并终止进程(),上述在程序调试时非常地有用。回到数据库中,这个断言也是差不多的,唯一的区别就是在数据库中断言失败后中断的不是程序进程,而是诸如过程()、函数()以及数据库操作(增删改)。
create assertion <断言名> <断言内容>;
CREATE ASSERTION RichPresCHECK(NOT EXISTS(SELECT Studio.name FROM Studio, MovieExecWHERE pres = cert AND netWorth < 10000000));
删除断言:drop assertion <断言名>;
CHECK和ASSERTION都是用来做一些检查的,那么它们有啥区别呢?
| 约束类型 | 声明位置 | (检查)动作发生的时机 | 确保成立? |
|---|---|---|---|
| 基于属性的CHECK | 属性 | 对关系插入元组或属性修改时 | 如果是子查询,则不能确保 |
| 基于元组的CHECK | 关系模式元素 | 对关系插入元组或属性修改时 | 如果是子查询,则不能确保 |
| 断言 | 数据库模式元素 | 对任何提及的关系做改变时 | 是 |
说白了,前两者是放在某个具体的表中的,因此仅在表数据修改时(更新UPDATE,添加INSERT,删除DELETE)检查,如果做的是查询(SELECT)则不影响表中数据,自然不会去检查;断言是放在某个数据库中且不存放于具体的某个表中的,因此balabala......
connect(buttonBox, SIGNAL(accepted()), this, SLOT(foo()));(其中指代自身,是一个对话框),那么在按钮的“确认”按钮被点击后,会发送()一个信号,而上述语句又会将该信号和对话框的函数连接,于是又会调用函数。回到数据库中,其触发器的触发和上述类似。“某个表被插入了一条新纪录”、“某个表有一条记录被更新”、“某个表中的某条记录被删除”等均为“事件”,如果声明了对应的触发器(可理解为响应函数),数据库系统()就会调用该触发器,执行里面的语句。
CREATE TRIGGER NetWorthTrigger -- 触发器名称为NetWorthTriggerAFTER UPDATE OF netWorth -- AFTER指定触发器在事件发生后触发,UPDATE指定事件为“发生记录的更新”,OF指定列,合起来就是“在列netWorth被更新后触发这个触发器”ON MovieExec -- 在哪个表上添加这个触发器REFERENCING -- 下面做一些别名(alias)设置OLD ROW AS OldTuple,NEW ROW AS NewTupleFOR EACH ROW -- FOR EACH ROW指定对每一行,执行下面的语句WHEN (OldTuple.netWorth > NewTuple.netWorth)UPDATE MovieExecSET netWorth = OldTuple.netWorthWHERE cert = NewTuple.cert;
注:不能完整地支持上述和的特性,其新记录直接使用名称,旧记录直接为,在需要指定属性(即指定列)时可以在触发器的部分使用如if (new.name <> old.name) then ...来替代解决。
一些解释
AFTER表示事件发生后触发,BEFORE表示事件发生时先执行触发器中语句再执行触发事件的(增、删、改)语句,INSTEAD OF表示事件触发时先执行触发器中语句然后忽略触发对应事件的(增、删、改)语句。分别称为AFTER触发器、BEFORE触发器和INSTEAD OF触发器。INSTEAD OF触发器与视图有关,MySQL不支持INSTEAD OF触发器。new row和old row;删除时有old row,而new row不可用;添加时仅new row可用。这是很自然的。FOR EACH ROW指定对每一行受影响的元组都执行后面的动作,称为“行级触发器”。如果不写或者写FOR EACH STATEMENT则为“语句级触发器”,无论受影响的元组有多少都仅执行一次。务必注意,默认为后者,因此即使你认为你要做的更新只针对某一行,也务必写上“FOR EACH ROW”,毕竟在DBMS看来,人家咋知道会不会有一个更新语句会更新到多个元组...触发器实例2
create trigger fix_yearbefore insert on moviesreferencingnew row as newrowold row as oldrowfor each rowwhen newrow.year is nullupdate newstuff set year = 1995;
create trigger avgtriggerafter update of netWorth on MovieExecreferencingold table as oldstuffnew table as newstufffor each statementwhen (60000 > (select avg(netWorth) from MovieExec))begindelete from MovieExecwhere (name, address, cert, netWorth) in newstuff;insert into MovieExec (SELECT * FROM oldstuff);end;
功能是如果更新记录后新的平均值过低(小于60000),则恢复之前的数据(删掉新加的数据,并添加进原有数据)。
create trigger avg_delete_triggerafter delete on MovieExecreferencingold table as oldstufffor each statementwhen (60000 > (select avg(netWorth) from MovieExec))begininsert into MovieExec (select * from oldstuff);end; -- 删除后平均值过低则恢复被删除数据create trigger avg_insert_triggerafter insert on MovieExecreferencingnew table as newstufffor each statementwhen (60000 > (select avg(netWorth) from MovieExec))begindelete from MovieExec where (name, address, cert, netWorth) in newstuff;end; -- 添加后平均值过低则删除添加进的数据