第五章 数据库完整性
数据库完整性包括:实体完整性,参照完整性,用户定义的完整性
【5.1】将Student表中的Sno属性定义为码
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) /*表级定义主码*/
);
实体完整性:主码不能为空。
CREATE TABLE 用PRIMARY KEY来设置主码,完成实体完整性。
在定义列时设定列为主码叫做列级定义主码,而在定义完表后再定义表的主码叫做表级定义主码,两种方式都可。
【5.2】将SC表中的Sno,Cno属性组定义为码
CREATE TABLE SC
(Sno CHAR(9) NOT NULL,
Cno CHAR(4) NOT NULL,
Grade SMALLINT,
PRIMARY KEY (Sno,Cno)
);
这个例子中只能在表级定义主码,因为主码是组合式的,单一的属性不能唯一代表一个对象,所以不能使用列级定义主码。
【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)
);
参照完整性规则:外码要么为空,要么为被参照关系的主码。
CREATE TABLE中用FOREIGN KEY短语定义哪些列为外码,用REFERENCES短语指明这些外码参照哪些表的主码。
参照完整性检查和违约处理:
对于被参照表Student和参照表SC有四种违约方式:
1.在参照表SC中插入元组,此元组的Sno属性值在被参照表中找不到一个元组。其Sno值与其相等,拒绝处理。
2.修改SC表中的一个元组,修改后该院组的Sno属性值在表Student中没有元组,其Sno属性值与之相等,拒绝处理。
3.从Student表中删除一个元组,SC中某些元组的Sno属性值在表Student中没有对应的相同Sno属性值元组。拒绝/级连删除/设置为空值。
4.修改Student表的Sno属性,使得SC表中某些元组的Sno属性在Student表中没有Sno属性对应,拒绝/级连删除/设置为空值。
处理策略:
1拒绝NO ACTION执行:不允许执行此操作,一般为默认策略。
2级联CASCADE操作:当删除和修改被参照表Student中的元组导致与参照表SC不一致时,删除或修改参照表中的所有导致不一致的元组。
3设置为空值:当删除和修改被参照表Student中的元组导致与参照表SC不一致时,将参照表中不一致的地方设置为空。
【5.4】显式说明参照完整性的违约处理示例
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)
ON DELETE CASCADE /*级联删除SC表中相应的元组*/
ON UPDATE CASCADE, /*级联更新SC表中相应的元组*/
FOREIGN KEY (Cno) REFERENCES Course(Cno)
ON DELETE NO ACTION /*当删除course 表中的元组造成了与SC表不一致时拒绝删除*/
ON UPDATE CASCADE /*当更新course表中的cno时,级联更新SC表中相应的元组*/
);
在将Sno,Cno属性作为SC的外码时,在后面加上ON DELECT(UPDATE) CASCADE(NO ACTION);
这里可以看到,若修改删除Student中的数据,则删除修改SC中相应的数据;修改Course中的Cno时,修改SC中的Cno,删除Course中的Cno时,不允许删除。
用户定义完整性:
1.属性上的约束条件:
列值非空(NOT NULL);列值唯一(UNIQUE);检查列值是否满足一个条件表达式(CHECK);
属性上的约束条件检查和违约处理:
插入元组或修改属性的值时,关系数据库管理系统检查属性上的约束条件是否被满足,如果不满足则操作被拒绝执行。
2.元组上的约束条件:
用CHECK短语定义元组上的约束条件,即元组级的限制。
元组上的约束条件检查和违约处理:
插入元组或修改属性的值时,关系数据库管理系统检查元组上的约束条件是否被满足,如果不满足则操作被拒绝执行。
【5.5】在定义SC表时,说明Sno、Cno、Grade属性不允许取空值。
CREATE TABLE SC
(Sno CHAR(9) NOT NULL,
Cno CHAR(4) NOT NULL,
Grade SMALLINT NOT NULL,
PRIMARY KEY (Sno, Cno),
);
Sno属性不允许取空值,Cno属性不允许取空值,Grade属性不允许取空值。
如果在标记定义实体完整性,则此时隐含Sno,Cno不能取空值。
【5.6】建立部门表DEPT,要求部门名称Dname列取值唯一,部门编号Deptno列为主码
CREATE TABLE DEPT
(Deptno NUMERIC(2),
Dname CHAR(9) UNIQUE NOT NULL,
Location CHAR(10),
PRIMARY KEY (Deptno)
);
Dname列的值唯一(UNIQUE), 并且不能取空值(NOT NULL).
Deptno不能为空。
【5.7】 Student表的Ssex只允许取“男”或“女”。
CREATE TABLE Studen
(Sno CHAR(9) PRIMARY KEY,
Sname CHAR(8) NOT NULL,
Ssex CHAR(2) CHECK (Ssex IN ('男','女')),
Sage SMALLINT
Sdept CHAR(20)
);
性别只能是 ‘男’或者‘女’。CHECK用来指定列值应该满足的条件。
INSERT
INTO Studen
VALUES('2014','xig','中',17,'M');
--INSERT 语句与 CHECK 约束"CK__Studen__Ssex__0B91BA14"冲突。该冲突发生于数据库"Student",表"dbo.Studen", column 'Ssex'。
【5.8】 SC表的Grade的值应该在0和100之间。
CREATE TABLE SC
(Sno CHAR(9) ,
Cno CHAR(4),
Grade SMALLINT CHECK (Grade>=0 AND Grade <=100),
PRIMARY KEY (Sno,Cno),
FOREIGN KEY (Sno) REFERENCES Student(Sno),
FOREIGN KEY (Cno) REFERENCES Course(Cno)
);
INSERT
INTO SC
VALUES('20121512 ','1',110);
--INSERT 语句与 CHECK 约束"CK__SC__Grade__0E6E26BF"冲突。该冲突发生于数据库"Student",表"dbo.SC", column 'Grade'。
【5.9】当学生的性别是男时,其名字不能以Ms.打头。
CREATE TABLE Student
(Sno CHAR(9),
Sname CHAR(8) NOT NULL,
Ssex CHAR(2),
Sage SMALLINT,
Sdept CHAR(20),
PRIMARY KEY (Sno),
CHECK (Ssex='女' OR Sname NOT LIKE 'Ms.%')
);
CHECK定义了Ssex和Sname的约束条件。
【5.10】建立学生登记表Student,要求学号在90000~99999之间,姓名不能取空值,年龄小于30,性别只能是“男”或“女”
CREATE TABLE Studen
(Sno NUMERIC(6)
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)
);
在Student表上建立了5个约束条件,包括主码约束(命名为StudentKey)以及C1、C2、C3、C4四个列级约束。
提示:数据库中已存在名为 ‘C1’ 的对象。忘了啥时候有过了,就把C1写成C1_.
约束一栏中没有C2,NULL和NOT NULL 是不被放在约束栏里的。
完整性约束命名子句:CONSTRAINT
包括NOT NULL、UNIQUE、PRIMARY KEY短语、FOREIGN KEY短语、CHECK短语等。
修改表中的完整性限制:使用ALTER TABLE语句修改表中的完整性限制。
【5.11】建立教师表TEACHER,要求每个教师的应发工资不低于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)
);
【5.12】去掉例5.10 Student表中对性别的限制
ALTER TABLE Studen
DROP CONSTRAINT C4;
【5.13】修改表Studen中的约束条件,要求学号改为在900000~999999之间,年龄由小于30改为小于40
ALTER TABLE Studen
DROP CONSTRAINT C1_;
ALTER TABLE Studen
ADD CONSTRAINT C1_ CHECK (Sno BETWEEN 900000 AND 999999);
ALTER TABLE Studen
DROP CONSTRAINT C3;
ALTER TABLE Studen
ADD CONSTRAINT C3 CHECK(Sage<40);
触发器:
任何用户对表的增、删、改操作均由服务器自动激活相应的触发器,触发器可以实施更为复杂的检查和操作,具有更精细和更强大的数据控制能力。
定义触发器格式:
CREATE TRIGGER
{BEFORE | AFTER} ON
REFERENCING NEW|OLD ROW AS
FOR EACH {ROW | STATEMENT}
[WHEN ]
触发事件:INSERT、DELECT或UPDATE;UPDATE OF,即进一步指明修改哪些列时激活触发器。
AFTER表示在触发事件的操作执行之后激活触发器,BEFORE表示在触发事件的操作执行之前激活触发器。
FOR EACH ROW:行级触发器(每一行触发一次);FOR EACH STATEMENT:语句级触发器;
【5.21】当对表SC的Grade属性进行修改时,若分数增加了10%则将此次操作记录到下面表中SC_U(Sno,Cno,Oldgrade,Newgrade),其中Oldgrade是修改前的分数,Newgrade是修改后的分数。
CREATE TRIGGER SC_T
AFTER UPDATE OF Grade ON SC
REFERENCING
OLD row AS OldTuple,
NEW row AS NewTuple
FOR EACH ROW
WHEN (NewTuple.Grade >= 1.1*OldTuple.Grade)
INSERT INTO SC_U(Sno,Cno,OldGrade,NewGrade)
VALUES(OldTuple.Sno,OldTuple.Cno,OldTuple.Grade,NewTuple.Grade)
--“AFTER”附近有语法错误。
创建好SC_U(Sno,Cno,Oldgrade,Newgrade)。执行上列语句后报错。
在T-SQL中,应写成:
CREATE TRIGGER SC_T
ON SC
AFTER
UPDATE
AS
--declare @变量名称 变量类型
declare @Sno char(15)
declare @Cno char(4)
declare @Oldgrade SMALLINT
declare @Newgrade SMALLINT
--IF(UPDATE(Grade)),返回一个布尔值,指示是否对表或视图的指定列进行了 INSERT 或 UPDATE 尝试。测试触发器是否应执行UPDATE操作。
IF(UPDATE(Grade))
SELECT @OLDgrade =Grade FROM DELETED
SELECT @NEWgrade =Grade FROM INSERTED
SELECT @SNO =Sno FROM SC
SELECT @CNO =Cno FROM SC
INSERT
INTO SC_U(Sno,Cno,OldGrade,NewGrade)
VALUES(@Sno,@Cno,@Oldgrade,@Newgrade);
数据测试:
UPDATE SC
SET Grade=100
WHERE Grade<100;
SELECT *
FROM SC_U
SC_U中结果,只有改后分数大于原来分数10%的记录。
【5.22】将每次对表Student的插入操作所增加的学生个数记录到表StudentInsertLog中。
--新建表,存储学生人数
CREATE TABLE StudentInsertLog
(
Numbers INT
)
--新建表,存储用户名和操作时间
CREATE TABLE StudentInsertLogUser
(
UserName nchar(10),
DateAndTime datetime
)
--新建触发器,当插入新的学生记录时,触发器启动,自动在StudentInsertLog记录学生人数
CREATE TRIGGER Student_Count
ON Student
AFTER
INSERT
AS
INSERT
INTO StudentInsertLog(Numbers)
SELECT COUNT(*)
FROM Student
--当插入新的学生记录时,触发器启动,自动在StudentInsertLogUser记录用户名和操作时间
CREATE TRIGGER Student_Time
ON Student
AFTER
INSERT
AS
declare @UserName nchar(10)
declare @DateTime datetime
select @UserName=system_user
select @DateTime=CONVERT(datetime,GETDATE(),120)
INSERT INTO StudentInsertLogUser(UserName,DateAndTime)
VALUES (@UserName,@DateTime)
--测试触发器效果
INSERT
INTO Student
VALUES ('201215135','王五','男',18,'CS');
SELECT * FROM Student
SELECT * FROM StudentInsertLog
SELECT * FROM StudentInsertLogUser
【5.23】定义一个BEFORE行级触发器,为教师表Teacher定义完整性规则“教授的工资不得低于4000元,如果低于4000元,自动改为4000元”
--创建Teacher表
CREATE TABLE Teacher
(
Name CHAR(10),
Salary SMALLINT,
Job CHAR(10)
)
--加入数据
INSERT
INTO Teacher(name,Salary,Job)
VALUES('清风',3500,'教授');
INSERT
INTO Teacher(name,Salary,Job)
VALUES('朗月',4500,'教授');
INSERT
INTO Teacher(name,Salary,Job)
VALUES('枝枝',3000,'副教授');
--创建触发器
CREATE TRIGGER Insert_Or_Update_Sal
ON Teacher
FOR INSERT , UPDATE
AS
declare @Salary SMALLINT
declare @Job CHAR(10)
IF(UPDATE(Salary))
SELECT @Salary=Salary FROM INSERTED
SELECT @Job=Job FROM Teacher
IF (@Job='教授' AND @Salary < 4000)
UPDATE Teacher
SET Salary=4000
WHERE Job='教授' AND Salary<4000
INSERT
INTO Teacher
VALUES('yuan',3200,'教授');
SELECT *
FROM Teacher
得到如下数据:
存储过程:由过程化SQL语句,经编译和优化后存储在数据库服务器中,可以被反复调用,运行速度较快。
【8.8】利用存储过程来实现下面的应用:从账户1转指定数额的款项到账户2中。
--建立新表Account,写入两个用户
DROP TABLE IF EXISTS Account;
CREATE TABLE Account
(
accountnum CHAR(3),
total FLOAT
);
INSERT INTO Account VALUES(101,50);
INSERT INTO Account VALUES(102,100);
SELECT * FROM Account
--建立存储过程
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
/*定义存储过程TRANSFER,参数为转入账户、转出账户、转账额度*/
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 = 101, --转入账户
@outAccount = 102, --转出账户
@amount = 50 --转出金额
SELECT * FROM Account
--余额不足情况
EXEC Proc_TRANSFER
@inAccount = 101, --转入账户
@outAccount = 102, --转出账户
@amount = 51 --转出金额
--账户存款不足
--账户不存在情况
EXEC Proc_TRANSFER
@inAccount = 100, --转入账户
@outAccount = 102, --转出账户
@amount = 50 --转出金额
--转入账户不存在
EXEC Proc_TRANSFER
@inAccount = 101, --转入账户
@outAccount = 103, --转出账户
@amount = 50 --转出金额
--转出账户不存在或账户中没有存款
【8.9】从账户01003815868转10000原到01003813828账户中
DROP TABLE IF EXISTS Account;
CREATE TABLE Account
(
accountnum CHAR(15), -- 账户编号
total FLOAT -- 账户余额
);
INSERT INTO Account VALUES(01003815868,20000)
INSERT INTO Account VALUES(01003813828,10000)
EXEC Proc_TRANSFER
@inAccount = 01003813828, --转入账户
@outAccount = 01003815868, --转出账户
@amount = 10000 --转出金额
SELECT * FROM Account
参考:
添加链接描述
添加链接描述
添加链接描述
添加链接描述
添加链接描述