数据库完整性是指数据的正确性和相容性。
数据的正确性:符合现实世界语义
数据的相容性:同一对象在不同关系中的数据是符合逻辑的
数据的完整性:防范对象:不合语义的、不正确的数据
数据的安全性:防范对象:非法用户和非法操作
实体完整性
CREATE TABLE中用PRIMAY KEY来定义为主码
检查主码值是否唯一 检查主码的各个属性是否为空
例5.1
将Student表中的Sno属性定义为码。
CREATE TABLE Student
(
Sno CHAR(9) PRIMARY KEY,
Sname CHAR(20) NOT NULL,
Ssex SMALLINT,
Sdept CHAR(20)
);
或者最后定义主码:
CREATE TABLE Student
(
Sno CHAR(9),
Sname CHAR(20) NOT NULL,
Ssex SMALLINT,
Sdept CHAR(20),
PRIMARY KEY(Sno)
);
例5.2
将SC表中的Sno,Cno属性定义为码。
CREATE TABLE SC
(
Sno CHAR(9) NOT NULL,
Cno CHAR(9) NOT NULL,
Grade SMALLINT,
PRIMARY KEY(Sno,Cno)
);
检查:
1、检查主码是否唯一,如果不唯一则拒绝插入或修改。
2、检查主码的各个属性是否为空,只要有一个为空就拒绝或修改。
参照完整性
CREATE TABLE中用FOREIGN KEY短语定义为外码,还应该定义外码列是否允许为空值。
FOREIGN KEY(Sno) REFERENCES Student(Sno)在表级定义参照完整性
主码不能设为空值,外码可以
例5.3
定义SC中的参照完整性
CREATE TABLE SC
(
Sno CHAR(9) NOT NULL,
Cno CHAR(9) NOT NULL,
Grade SMALLINT,
PRIMARY KEY(Sno,Cno)
FOREIGN KEY(Sno) REFERENCES Student(Sno),
FOREIGN KEY(CNO) REFERENCES Course(Cno)
);
处理策略:
1、拒绝执行(默认策略)
2、级联操作
3、设置为空值
例5.4
显示说明参照完整性的违约处理实例。
CREATE TABLE SC
(
Sno CHAR(9),
Cno CHAR(9),
Grade SMALLINT,
PRIMARY KEY(Sno,Cno)
FOREIGN KEY(Sno) REFERENCES Student(Sno)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY(CNO) REFERENCES Course(Cno)
ON DELETE NO ACTION
ON UPDATE CASCADE
);
显示说明 ON DELETE CASCADE级联删除
ON DELETE NO ACTION 拒绝删除
ON UPDATE CASCADE 级联更新
用户定义的完整性
针对某一具体应用的数据必须满足的语义要求
属性上的:NOT NULL(列值非空)、UNIQUE(列值唯一)、CHECK(检查列值是否满足一个条件)
例:CHECK (Ssex IN (‘男’,’女’))
1、不允许取空值
例5.5
在定义SC表时,说明Sno,Cno,Grade属性不允许为空值。
CREATE TABLE SC
(
Sno CHAR(9) NOT NULL,
Cno CHAR(9) NOT NULL,
Grade SMALLINT NOT NULL,
PRIMARY KEY(Sno,Cno)
FOREIGN KEY(Sno) REFERENCES Student(Sno),
FOREIGN KEY(CNO) REFERENCES Course(Cno)
);
2、列值唯一
例5.6
建立部门表DEPT,要求部门名称Dname列值唯一,部门编码Deptno列为主码。
CREATE TABLE DEPT
(
Deptno NUMERIC(2),
Dname CHAR(9) UNIQUE NOT NULL,
Dlocation CHAR(10),
PRIMARY KEY(Deptno)
);
3、用CHECK短语指定列值应该满足的条件
例5.7
Student表的Ssex只允许取“男”或“女”。
CREATE TABLE Stuent
(
Sno CHAR(9) PRIMARY KEY,
Sname CHAR(8) NOT NULL,
Sname CHAR(2) CHECK(Ssex IN('男','女')),
Sage SMALLINT,
Sdept CHAR(20)
);
例5.8
SC表的Grade的值应该在0和100之间。
在这里插入代码片
CREATE TABLE SC
(
Sno CHAR(9),
Cno CHAR(9),
Grade SMALLINT CHECK(Grade>=0 AND Grade <=100),
PRIMARY KEY(Sno,Cno)
FOREIGN KEY(Sno) REFERENCES Student(Sno),
FOREIGN KEY(CNO) REFERENCES Course(Cno)
);
元组上的约束条件
在CREATE TABLE语句中可以用CHECK短语定义元组上的约束条件,即元组级的限制。
例5.9
当学生的性别是男时,其名字不能以Ms.打头。
CREATE TABLE Stuent
(
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.%')
);
完整性约束命名子句
完整性约束命名子句
CONSTRAINT<完整性约束条件名><完整性约束条件>
完整性约束条件包括NOT NULL,UNIQUE,PRIMARY,FOREIGN KEY,CHECK短语等。
名字用到就起名,用不到系统也会自定义
例5.10
建立学生登记表Student,要求学号在90000~99999之间,姓名不能取空值,年龄小于30,性别只能是“男”或“女”。
CREATE TABLE Stuent
(
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(Sage IN('男','女')),
CONSTRAINT StudentKey PRIMARY KEY(Sno)
);
例5.11
建立教师表TEACHER,要求每个教师的应发工资不低于3000元。应发工资列Sal与扣除项Deduct之和。
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 TEACHERFKey FOREIGN KEY(Deptno)
REFERENCES DEPT(Deptno),
CONSTRAINT C1 CHECK(Sal+Deduct >= 3000)
);
Numeric(7,2)表示总位数为7,小数点后为2位的数,也就是说这个字段的整数位最大是8位。
NUMERIC数据类型使用标准、可变长度的内部格式来存储数字。
修改完整性约束条件
可以使用ALTER TABLE语句修改表中的完整性规则。
例5.12
去掉例5.10Student表中对性别的限制。
ALTER TABLE Student
DROP CONSTRAINT C4;
例5.13
修改Student中的约束条件,要求学号改为在90000~99999之间,年龄由小于30改为小于40.
ALTER TABLE Student
DROP CONSTRAINT C1;
ALTER TABLE Student
ADD CONSTRAINT C1 CHECK(Sno BETWEEN 90000 AND 99999);
ALTER TABLE Student
DROP CONSTRAINT C3;
ALTER TABLE Student
DROP CONSTRAINT C3 CHECK (Sage<40);
触发器是用户定义在关系表上的一类由事件驱动的特殊过程。一旦定义,触发器将被保存在数据库服务器中。
定义触发器
CREATE TRIGGER <触发器名>
/*每当触发事件发生时,该触发器被激活*/
{BEFORE|AFTER}<触发事件> ON <表名>
/*指明触发器激活的时间是在执行触发事件前或后*/
REFERENCING NEW|OLD ROW AS<变量>
/*REFERENCING指出引发的变量*/
FOR EACH {ROW|STATEMENT}
/*定义触发器的类型,指明动作执行的频率*/
[WHEN <触发条件>]<触发动作体>
/*仅当触发条件为真时才执行触发动作体*/
1、只有创建表的用户才可以在表上创建触发器,并且一个表上只能创建一定数量的触发器。
2、触发器名
同一模式下,触发器名必须是唯一的,并且触发器名和表名必须在同一模式下。
3、表名
触发器只能定义在基本表上,不能定义在视图上。
4、触发事件
触发事件—INSERT\DELETE\UPDATE
UPDATE FOR<触发列,。。。。>
5、触发器类型
行级触发器(FOR EACH ROW) 100次
语句触发器 一次
6、触发条件
触发器被激活时,只能当触发条件为真时触发动作才执行,否则触发动作体不执行,如果忽略WHEN触发条件,则触发动作在触发器激活后立即执行。
7、触发动作体
触发动作体既可以是一个匿名PL\SQL动作块,也可以是对已创建存储过程的调用。
如果触发动作体执行失败,激活触发器的事件(即对数据库的增、删、该操作)就会终止执行,触发器的目标表或触发器可能影响的其他对象不发生任何变化。
BEGIN({)
IF AND
THEN(然后怎么样);
END IF;
END;(})
例5.21
当对表SC的Grade属性进行修改时,若分数增加了10%,则将此次操作记录到另一个表SC_U(Sno,Cno,Oldgrade,Newgrade)中,其中Oldgrade是修改前的分数,Newgrade是修改后的分数。
先建表SC_U
CREATE TABLE SC_U
(
Sno CHAR(9) PRIMARY KEY,
Cno CHAR(9),
OldGrade SMALLINT,
NewGrade SMALLINT
);
CREATE TRIGGER SC_T
/*SC_T是触发器的名字*/
AFTER UPDATE OF Grade ON SC
/*UPDATE OF Grade ON SC是触发事件*/
/*AFTER是触发的时机,表示当时对SC的Grade属性修改完后再触发下面的规则*/
REFERENCING
OLDROW AS OldTuple,
NEWROW AS NewTuple,
FOR EACH ROW
/*行级触发器,即每执行一次Grade的更新,下面的规则就执行一次*/
WHEN(NewTuple.Grade >= 1.1*OldTuple.Grade)
/*触发条件,只有该条件为真时才执行*/
/*下面的insert操作*/
INSERT INTO SC_U(Sno,Cno,OldGrade,NewGrade)
VALUES(OldTuple.Sno,OldTuple.Cno,Grade,NewTuple.Grade);
报错:“AFTER”附近有语法错误。
此地方是因为T-SQL与标准SQL的写法不同,因改为
CREATE TABLE SC_U
(
Sno CHAR(9) PRIMARY KEY,
Cno CHAR(9),
OldGrade SMALLINT,
NewGrade SMALLINT
);
CREATE TRIGGER SC_T
ON SC
FOR UPDATE
AS
declare @OLD SMALLINT
declare @NEW SMALLINT
declare @SNO CHAR(9)
declare @CNO CHAR(4)
IF(UPDATE(Grade))
BEGIN
select @OLD =Grade FROM DELETED
select @NEW =Grade FROM INSERTED
select @SNO =Sno FROM SC
select @Cno =Cno FROM SC
IF(@NEW>=1.1*@OLD)
INSERT INTO SC_U(Sno,Cno,Oldgrade,Newgrade)
VALUES(@SNO,@CNO,@OLD,@NEW)
END
测试:
UPDATE SC
SET GRADE=70
WHERE Sno='201215121' AND Cno='3'
UPDATE SC
SET GRADE=95
WHERE Sno='201215122' AND Cno='3'
SELECT * FROM SC
SELECT * FROM SC_U
例5.22
将每次对表Student的操作所增加的学生个数记录到表Sutdent-InsertLog中。
/*新建StudentInsertLog表来存储学生人数*/
CREATE TABLE StudentInsertLog
(
Number INT
)
/*新建StudentInsertLogUser表来存储用户名和操作时间*/
CREATE TABLE StudentInsertLogUser
(
UserName nchar(10),
DateAndTime datetime
)
/*新建触发器Student_Count,当插入新的学生记录时,触发器启动,自动在StudentInsertLog记录学生人数*/
CREATE TRIGGER Student_Count
ON Student
AFTER
INSERT
AS
INSERT INTO StudentInsertLog(Number)
SELECT COUNT(*) FROM Student
/*新建触发器Student_Time,当插入新的学生记录时,触发器启动,自动在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) --2018-04-11 16:33:10
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
(
Tno CHAR(9),
Tname CHAR(9),
Job CHAR(9),
Salary SMALLINT
);
CREATE TRIGGER Insert_Or_Update_Sal
BEFORE INSERT OR 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
IF UPDATE(Salary)
BEGIN
declare @TNO CHAR(9)
declare @TNAME CHAR(9)
declare @JOB CHAR(9)
declare @SALARY SMALLINT
select @SALARY = Salary FROM INSERTED
select @TNO = Tno FROM Teacher
select @TNAME = Tname FROM Teacher
select @JOB = Job FROM Teacher
IF(@SALARY<4000 AND @JOB='教授' )
UPDATE Teacher
SET Salary=4000
WHERE Salary<4000 AND Job='教授'
END
测试数据
INSERT
INTO Teacher
VALUES('2020007','黑黑','教授',3200)
SELECT * FROM Teacher
激活触发器
触发器执行,是由触发事件激活的,并由数据库服务器自动执行
删除触发器
DROP TRIGGER<触发器名>;
存储过程
有过程SQL语句,经编译和优化后存在数据库服务器中,可以被反复调用,运行速度较快。
优点:
1、效率高
2、降低客户机和服务器之间的通信量
3、方便实施企业规则
创建 执行 修改 删除
创建存储过程
CREATE OR REPLACE PROCEDURE 过程名([参数1,参数2,...])
/*存储过程首部*/
AS <过程化SQL块>;(例如 DECLARE 定义变量)
/*存储过程体,描述该存储过程的操作*/
例8.8
利用存储过程实现下面的应用:从账户1转指定数额的款项到账户2中。假设账户关系表为Account(Accountnum,Total).
1、建立新表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 = 102, --转入账户
@outAccount = 101, --转出账户
@amount = 200 --转出金额
SELECT * FROM Account
EXEC Proc_TRANSFER
@inAccount = 103, --转入账户
@outAccount = 101, --转出账户
@amount = 50 --转出金额
SELECT * FROM Account
例8.9
从账户01003815868 转10000元到01003813828账户中
SELECT * FROM Account
INSERT INTO Account VALUES(01003815868,20000)
INSERT INTO Account VALUES(01003813828,10000)
EXEC Proc_TRANSFER
@inAccount = 01003813828, --转入账户
@outAccount = 01003815868, --转出账户
@amount = 10000 --转出金额
SELECT * FROM Account
参考:
https://blog.csdn.net/qq_38975453/article/details/104729884
https://blog.csdn.net/qq_38975453/article/details/104729681
这一篇给我的感觉就是:轻松----->绝望,刚开始写完整性的时候,感觉so easy,都是之前说过的,再写一遍就可以了,然后轻松的过渡到触发器,刚开始定义方面就花费了很多事件,到了例题,标准SQL再SQL SERVER中不能用,又百度了一下,参考别人家的博客,勉勉强强地写下来,到了存储过程,题目很简单,但是要建立存储过程,便变得复杂起来,最后终于迎来了尾声,还是感觉自己棒棒哒!!!