数据库的完整性是指数据的正确性和相容性。
正确性:数据是符合现实世界语义、反应当前实际情况的;
相容性:数据库同一对象在不同关系表中的数据是复合逻辑的。
在CREATE TABLE中用 PRIMARY KEY定义。
分为列级约束条件和表级约束条件。
5.1
create table Student
(Sno char(9) primary key,--在列级定义主码
Sname char(20) not null,
Ssex char(2),
Sage smallint,
Sdept char(20));
--或者
create table Student
(Sno char(9),
Sname char(20) not null,
Ssex char(2),
Sage smallint,
Sdept char(20)
primary key(Sno));--在表级定义主码
5.2
create table SC
(Sno char(9) not null,
Cno char(4) not null,
Grade smallint,
primary key (Sno,Cno)--因为是两个外码作为主码,所以只能在表级定义主码
);
定义主码后的检查内容:
(1)检查主码值是否唯一,如果不唯一则拒绝插入或修改。
(2)检查主码的各个属性是否为空,只要有一个为空就拒绝插入或修改。
关系模型的参照完整性在CREATE TABLE 中用FOREIGN KEY短语定义那些列为外码,用REFERENCES短语指明这些外码参照那些表的主码。
【例5.3】定义SC中的参照完整性
create table SC
(Sno char(9) not null,
Cno char(4) not null,
Grade smallint,
primary key (Sno,Cno),/*在表级定义实体完整性*/
foreign key(Sno)references Student(Sno),/*在表级定义参照完整性*/
foreign key(Cno)references Course(Cno)/*在表级定义参照完整性*/
);
【例5.4】显示说明参照完整性的违约处理示例
create table SC
(Sno char(9) not null,
Cno char(4) not null,
Grade smallint,
primary key (Sno,Cno),/*在表级定义实体完整性,Sno、Cno都不能取空值*/
foreign key(Sno) references Student(Sno),/*在表级定义参照完整性*/
on delete cascade /*删除Student元组时,级联删除SC表中相应的元组*/
on update cascade /*Student表更新Sno时,级联更新SC表中相应的元组*/
foreign key(Cno) references Course(Cno)/*在表级定义参照完整性*/
on delete no action /*当删除Course表中的元组导致与SC表不一致时拒绝删除*/
on update cascade /*当更新Course表中的Cno时级联更新SC表中相应的元组*/
);
用户定义的完整性就是针对某一具体应用的数据必须满足的语言要求。
属性值限制:
列值非空(not null)
列值唯一(unique)
检查列值是否满足一个条件表达式(check短语)
(1)不允许空值
create table SC
(Sno char(9) not null,--Sno不能为空
Cno char(4) not null,--Cno不能为空
Grade smallint not null,--Grade不能为空
primary key (Sno,Cno)
);
(2)列值唯一
create table DEPT
(Deptno numeric(2),
Dname char(9) unique not null, --Dname列值唯一,且不能为空
Location_ char(10),
primary key(Deptno)--Deptno是主码
);
check短语:
(3)用CHECK短语指定列值应满足的条件。
【例5.7】Student表的Ssex只允许取“男”或女“。
create table Student
(Sno char(9) primary key,
Sname char(20) not null,
Ssex char(2) check(Ssex in('男','女')),--性别只允许取男或女两种
Sage smallint,
Sdept char(20));
【例5.8】SC表的Grade的值应该在0~100之间。
create table SC
(Sno char(9) not null,
Cno char(4) not null,
Grade smallint check(Grade>=0 and Grade<=100),--成绩范围在0到100之间
primary key (Sno,Cno),
foreign key(Sno)references Student(Sno),
foreign key(Cno)references Course(Cno)
);
【例5.9】当学生的性别是男时,其名字不能以Ms.打头。
create table Student
(Sno char(9),
Sname char(20) not null,
Ssex char(2),
Sage smallint,
Sdept char(20),
primary key(Sno),
check(Ssex='女'or Sname not like 'Ms.%')
);
check对元组进行检查,当性别是女时可通过,或者性别不是女(性别为男)但名字没有以Ms. 打头也可通过。
CONSTRAINT <完整性约束条件名><完整性约束条件>
完整性约束条件:not null,unique,primary key,foreign key,check 短语等。
【例5.10】建立学生登记表,要求学号在90000~99999之间,姓名不能取空值,年龄小于30,性别只能是男或女。
create table Student
(Sno numeric(9)
constraint C1 check(Sno between 90000 and 99999),
Sname char(20)
constraint C2 not null,
Sage numeric(3)
constraint C3 check(Sage<30),
Ssex char(2)
constraint C4 check(Ssex in('男','女')),
constraint StudentKey primary key(Sno)
);
【例5.11】建立教师表Teachear,要求每个教师的应发工资不低于3000员。
create table Teacher
(Eno numeric(4) primary key,
Ename char(10),
Job char(8),
Sal numeric(7,2),
Deduct numeric(7,2),
Deptno numeric(2),
constraint TeacherKey foreign key (Deptno) references DEPT(Deptno),
constraint C1 check(Sal+Deduct>=3000)
);
使用ALTER TABLE语句修改表中的完整性限制。
【例5.12】去掉例5.10Student表中对性别的限制。
alter table Student
drop constraint C4;
【例5.13】修改表Student中的约束条件,学号改在900000~999999之间,年龄小于40.
因为原先有约束条件,可以先删除原来的,再添加新的约束条件。
drop删除,add添加
alter table Student
drop constraint C1;
alter table Student
add constraint C1 check(Sno between 900000 and 999999);
alter table Student
drop constraint C3;
alter table Student
add constraint C3 check (Sage<40);
CREATE ASSERTION <断言名>
【例5.18】限制数据库课程最多60名学生选修。
create assertion ASSE_SC_DB_NUM
check(60>=(select count(*)from SC,Course
where SC.Cno=Course.Cno and Course.Cname='数据库')
);
每对SC表插入一组元组,断言都会被除触发检查,直到达60人时会拒绝插入。
【例5.19】限制每一门课程最多60名学生选修。
create assertion ASS_SC_CNUM1
check(60>=all(select count(*) from SC group by Cno)
);
先按照课程进行分组,再检查每个课程的人数。
【例5.20】限制每个学期每一门课程最多60名学生选修。
alter table SC add term date;--增加term属性,类型是date
create assertion ASSE_SC_CNUM2
check(60>=all(select count(*)from SC group by Cno,term)
);
DROP ASSERTION <断言名>;
SQL中有断言,但在T-SQL中是不能使用断言的。
SQL建立触发器的一般语句为:
CREATE TRIGGER <触发器名> --每当触发事件发生时,该触发器被激活
{BEFORE|AFTER}<触发事件>ON<表名> --指明触发器激活的时间
REFERENCING NEW|OLD ROW AS <变量> --REFERENCING指出引用的变量
FOR EACH{ROW|STATEMENT} --定义触发器的类型,指明动作体执行的频率
[WHEN <触发条件>]<触发动作体> --仅当触发器条件为真时才执行触发动作体
当特定的系统事件(如对一个表的增、删、改操作,事务的结束等)发生时,对条件进行检查,满足条件执行动作。
a. 触发事件可以是INSERT,DELETE或UPDATE,也可以是这几个事件的组合,也可以是UPDATE OF<触发列,…>
b. 触发器类型有行级触发器和语句级触发器
【例5.21】当对SC表的Grade属性进行修改时,若分数增加了10%,则将操作记录到另一个表中。
标注SQl语句是这么写的:
create table SC_U
(Sno char(9),
Cno char(4),
Oldgrade smallint,
Newgrade smallint);
create trigger SC_T
alter update of Grade on SC
referencing
old row as OldTuple,
new row as NewTuple,
for ecah row
when (NewTuple.Grade>=1.1*OldTuple)
insert into SC_U(Sno,Cno,OldGrade,NewGrade)
values(OldTuple.Sno,OldTuple.Cno,OldTuple.Grade,NewTuple.Grade);
T-Sql语句:
create trigger SC_T
on SC after update
as
declare @Sno char(9),
@Cno char(4),
@NewGrade smallint,
@OldGrade smallint;
select @Sno=Sno,@Cno=Cno from inserted;
select @NewGrade=Grade from inserted;
select @OldGrade=Grade from deleted;
if(@NewGrade>=1.1*@OldGrade)
begin
insert into SC_U(Sno,Cno,OldGrade,NewGrade)
values(@Sno,@Cno,@OldGrade,@NewGrade)
end
更改数据时:
update SC
set Grade=100
where Sno='201215126';
select * from SC;
select * from SC_U;
他原先的成绩是50
在T-Sql中declare取代了referencing的作用,inserted是新的数据,deleted是获得旧的数据。
【例5.22】将每次对表Student 的插入操作所增加的学生个数记录添加到表Student-InsertLog 中。
SQl:
create trigger Student_Count
after insert on Student
referencing
new table as delta
for each statement
insert into Student_InsertLog(Numbers)
select count(*) from delta;
T-Sql:
create trigger Student_Count
on Student after insert
as
insert into Student_InsertLog(Numbers)
select count(*) from Student;
【例5.23】为教师表定义完整性规则下“教授的工资不得低于4000元,如果低于4000元,自动改为4000元”。
Sql:
create trigger Insert_Or_Update_Sal
before insert ot update on Teacher
referencing new row as newTuple
for each row
begin
if(newTuple.Job='教授') and (newTuple.Sal<4000)
then newTuple.Sal:=4000;
end if;
end;
T-Sql
create trigger Insert_Or_Update_Sal
on Teacher for insert,update
as
declare @Eno numeric(4,0),
@Ename varchar(10),
@Job varchar(8),
@Sal numeric(7,2),
@Deduct numeric(7,2),
@Deptno numeric(2,0);
select @Eno=Eno,@Ename=Ename,@Job=Job,@Sal=Sal,@Deduct=Deduct,
@Deptno=Deptno,from inserted;
if(@Sal<4000 and @Job='教授')
begin
update Teacher set @Sal=4000
end;
执行顺序:
(1)执行该表上的before触发器
(2)激活触发器的SQL语句
(3)执行该表上的AFTER触发器
DROP TRIGGER <触发器名>ON <表名>;
创建存储过程:
CREATE OR REPLACE PROCEDURE 过程名 ([参数1,参数2,…])
AS<过程化SQL块>;
IF (exists (select * from sys.objects where name = 'Proc_Transfer'))
DROP PROCEDURE Proc_Transfer
go
create procedure Proc_Transfer
--定义存储过程,参数为转入账户、转出账户、转账额度
@inAccount int,
@outAccount int,
@amount float
as
begin transaction trans
declare--定义变量
@totalDepositOut float,
@totalDepositIn float,
@inAccountnum int;
select @totalDepositOut=total from Account where accountnum=@outAccount;--检查转出账户
if(@totalDepositOut is null)
begin
print '转出账户不存在或没有余额'
rollback
transaction trans;
return;
end;
if(@totalDepositOut<@amount)
begin
print'余额不足'
rollback
transaction trans;
return;
end;
select @inAccountnum=accountnum from Account where accountnum=@inAccount;--检查转入账户
if(@inAccountnum is null)
begin
print '转入账户不存在'
rollback
transaction trans;
return ;
end;
begin
--修改金额
update Account set total=total-@amount where accountnum=@outAccount;
update Account set total=total+@amount where accountnum=@inAccount;
print '转账完成'
commit transaction trans;
return;
end;
exec Proc_Transfer
@inAccount = 00000, --转入账户
@outAccount = 00001, --转出账户
@amount = 50 --转出金额
select * from Account
修改存储过程:
ALTER PROCEDURE 过程名1 RENAME TO 过程名2;
也可以重新编译一个存储过程:
ALTER PROCEDURE 过程名 COMPILE;
删除存储过程:
DROP PROCEDURE 过程名();
耗费了一节课和一下午终于写完了,第一感觉是好累啊,SQL与T-Sql在某些方面差别真的好大,还有,存储过程有点难啊。