@3013216027
2016-01-28T13:18:29.000000Z
字数 6141
阅读 1076
数据库原理复习专栏
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 null
on 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 movies
add foreign key (producerC#) references MovieExec(name)
alter table Movies
add foreign key (producerC#) references MovieExec(name)
on update set null
on delete set null
alter table Movies
add foreign key (producerC#) references MovieExec(name)
on update cascade
on delete cascade
alter table StarsIn
add foreign key (moveieTitle) references Movies(title)
alter table StartsIn
add 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 RichPres
CHECK(NOT EXISTS
(SELECT Studio.name FROM Studio, MovieExec
WHERE 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 -- 触发器名称为NetWorthTrigger
AFTER UPDATE OF netWorth -- AFTER指定触发器在事件发生后触发,UPDATE指定事件为“发生记录的更新”,OF指定列,合起来就是“在列netWorth被更新后触发这个触发器”
ON MovieExec -- 在哪个表上添加这个触发器
REFERENCING -- 下面做一些别名(alias)设置
OLD ROW AS OldTuple,
NEW ROW AS NewTuple
FOR EACH ROW -- FOR EACH ROW指定对每一行,执行下面的语句
WHEN (OldTuple.netWorth > NewTuple.netWorth)
UPDATE MovieExec
SET netWorth = OldTuple.netWorth
WHERE 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_year
before insert on movies
referencing
new row as newrow
old row as oldrow
for each row
when newrow.year is null
update newstuff set year = 1995;
create trigger avgtrigger
after update of netWorth on MovieExec
referencing
old table as oldstuff
new table as newstuff
for each statement
when (60000 > (select avg(netWorth) from MovieExec))
begin
delete from MovieExec
where (name, address, cert, netWorth) in newstuff;
insert into MovieExec (SELECT * FROM oldstuff);
end;
功能是如果更新记录后新的平均值过低(小于60000),则恢复之前的数据(删掉新加的数据,并添加进原有数据)。
create trigger avg_delete_trigger
after delete on MovieExec
referencing
old table as oldstuff
for each statement
when (60000 > (select avg(netWorth) from MovieExec))
begin
insert into MovieExec (select * from oldstuff);
end; -- 删除后平均值过低则恢复被删除数据
create trigger avg_insert_trigger
after insert on MovieExec
referencing
new table as newstuff
for each statement
when (60000 > (select avg(netWorth) from MovieExec))
begin
delete from MovieExec where (name, address, cert, netWorth) in newstuff;
end; -- 添加后平均值过低则删除添加进的数据