数据库完整性

1.实体完整性

CREATE TABLE中用PRIMARY KEY定义,可在列级,表级完整性条件中定义,如【例5.1】。涉及到多属性作为码的时候,只能放在最后表级完整性条件中,如【例5.2】。

规则:主码唯一,不为空
插入和更新时DBMS会根据这一规则进行检查,不唯一或为空就拒绝。

【例5.1】将Student表中的Sno定义为主码

(1)列级定义主码

CREATE TABLE Student
(Sno CHAR(9) PRIMARY KEY,
Sname CHAR(20) NOT NULL,
Ssex CHAR(2),
Sage SMALLINT,
Sdept CHAR(20)
);

(2)表级定义主码

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)
);

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 Student(Cno)
);

参照完整性规则:外码要么为空,要么取自被参照关系的主码
插入或修改时,DBMS会进行检查,并做出违约处理,其中的违约处理也可以显式给出。

设置为空值的情况,一般用于外码在该关系中不是主码,并且没有MOT NULL约束的情况,可以设为空值。

【例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 Student(Cno)ON DELETE NO ACTION  --拒绝删除ON UPDATE CASCADE --级联更新
);

注意在定义外码语句和违约处理语句之间没有逗号,两个违约处理语句之间没有逗号。

3.用户定义的完整性

针对某一具体应用的数据必须满足的语义要求

  • 属性上的约束条件

列级完整性
定义:NOT NULL(列值非空),UNIQUE(取值唯一),CHECK(检查列值是否满足条件)

(1)NOT NULL 非空
【例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),
...

(2)UNIQUE 取值唯一
【例5.6】建立部门表DEPT,要求部门名称Dname列取值唯一,部门编号Deptno为主码

CREATE TABLE DEPT
(Deptno NUMERIC(2) PRIMARY KEY,
Dname CHAR(9) UNIQUE,
Location CHAR(10)
);

(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),
PRIMARY KEY(Sno,Cno),
FOREIGN KEY Sno REFERENCES Student(Sno),
FOREIGN KEY Cno REFERENCES Student(Cno)
);

CHECK短语相当于WHERE子句,加条件。

  • 元组上的约束条件

在CREATE TABLE 时用CHECK短语定义,元组级的限制,涉及多个列,表级完整性
【例5.9】当学生的性别是男时,其名字不能用‘Ms.’打头

CREATE TABLE Student
(Sno CHAR(9) PRIMARY KEY,
Sname CHAR(8) NOT NULL,
Ssex CHAR(2),
Sage SMALLINT,
Sdept CHAR(20),
CHECK (Ssex='女' OR Sname NOT LIKE 'Ms.%')
--涉及到了两个属性值 Ssex和Sname,对元组的限制
);

学生表中,要么性别为女,要么名字不是Ms.打头的

元组上和属性上的约束条件,在插入、更新元组或属性值时,DBMS都会检查是否满足条件,不满足则拒绝。

4.完整性约束命名子句

命名:

CONSTRAINT <完整性约束条件名> <完整性约束条件>

给约束命名,便于查找,修改和删除。
完整性约束条件可以为 NOT NULL,UNQIUE,CHECK短语,PRIMARY KEY短语,FOREIGN KEY短语等

【例5.10】建立学生登记表Student,要求学号在90000~99999之间,姓名不能取空值,年龄小于30,性别只能是‘男’或‘女’。

CREATE TABLE Student1
(Sno NUMERIC(6) CONSTRAINT C1 CHECK(Sno BETWEEN 90000 AND 99999),
Sname CHAR(20) CONSTRAINT C2 NOT NULL,
Ssex CHAR(2) CONSTRAINT C3 CHECK (Ssex IN ('男','女')),
Sage SMALLINTCONSTRAINT C4 CHECK(Sage<30),
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 TEACHERKey FOREIGN KEY (Deptno)REFERENCES DEPT(Deptno),
CONSTRAINT C5 CHECK(Sal + Deduct >= 3000)
--元组上的约束条件
);

要与上题中建立的Student1在同一个库中的话,最后的约束名应该修改,因为在Student1中已经有了该约束名。

外码和主码的约束直接标注在列里。

修改:
ALTER TABLE修改表中的完整性限制

【例5.12】去掉5.10中Student1表中的对性别的限制

ALTER TABLE Student1
DROP CONSTRAINT C3;

测试,可以插入除 男、女 之外的值,删除成功。

【例5.13】修改Student1表中的条件,要求学号改为900000~999999之间,年龄在由小于30改为小于40.

先删除原来的约束条件,再增加新的条件。

ALTER TABLE Student1
DROP CONSTRAINT C1;
ALTER TABLE Student1
ADD CONSTRAINT C1 CHECK(Sno BETWEEN 900000 AND 999999);
ALTER TABLE Student1
DROP CONSTRAINT C4;
ALTER TABLE Student1
ADD CONSTRAINT C4 CHECK(Sage < 40);

这里的删除可以一起删除,添加也可以一起添加:

ALTER TABLE Student1
DROP CONSTRAINT C1,C4;
ALTER TABLE Student1
ADD CONSTRAINT C1 CHECK(Sno BETWEEN 900000 AND 999999),CONSTRAINT C4 CHECK(Sage < 40);


语句是没有错误的,这里会出现错误是因为在5.12中测试的时候添加了学号为90001的记录,如果修改约束性条件,使得表中的数据不满足,则拒绝添加这个约束性条件。

5.触发器

定义触发器:
语句:

CREATE TRIGGER <触发器名>
{BEFORE|AFTER} <触发事件> ON <表名>
REFERENCING NEW|OLD ROW AS <变量>
FOR EACH {ROW|STATEMENT}
[WHEN <触发条件>]<触发动作体>

当特定的系统事件发生时,对规则的条件进行检查,如果条件成立则执行规则中的动作。
1.AFTER|BEFORE是触发的时机。
2.ON 后的<表名> 表示是建立在哪个表上的触发器。
3.AS后的变量,相当于给NEW ROW或者OLD ROW起的别名,便于在后面的触发条件和动作体中使用。
4.触发事件可以是INSERT/UPDATE/DELETE,也可以是他们的组合。还可以用UPDATE OF <触发列,…>,指明修改哪些列时触发。
5.触发器类型:

  • 行级触发器(FOR EACH ROW)
  • 语句级触发器(FOR EACH STATEMENT)


【例5.21】当对表SC的Grade属性进行修改时,若分数增加了10%,则将此次操作记录到SC_U表中:
SC_U(Sno,Cno,Oldgrade,Newgrade)

--1.创建SC_U表:
CREATE TABLE SC_U
(Sno CHAR(10) NOT NULL,
Cno CHAR(5) NOT NULL,
Oldgrade SMALLINTCONSTRAINT C6 CHECK(Oldgrade BETWEEN 0 AND 100),
Newgrade SMALLINTCONSTRAINT C7 CHECK(Newgrade BETWEEN 0 AND 100),
CONSTRAINT SCKEY PRIMARY KEY(Sno,Cno)
);--2.定义触发器:
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_UVALUES(OldTuple.Sno,OldTuple.Cno,OldTuple.Grade,NewTuple.Grade);

【例5.22】将每次对表Student的插入操作所增加的学生个数记录到表StudentInsertLog中。

--1.建表
CREATE TABLE StudentInsertLog
(Numbers INT
);

标准SQL:

CREATE TRIGGER Student_Count
AFTER INSERT ON Student
REFERENCINGNEW TABLE AS DELTA
FOR EACH STATEMENTINSERT INTO StudentInsertLogSELECT COUNT(*) FROM DELTA--带子查询的插入,将子查询结果插入到表中

这里插入的是新表DELTA的元组个数,指的是增加的个数。

T-SQL:

创建触发器,记录学生人数:

CREATE TRIGGER Student_Count
ON Student
AFTER
INSERT
AS INSERT INTO StudentInsertLog(Numbers)SELECT COUNT(*) FROM Student

建立一个存储用户名和时间的表,相当于审计日志。
StudentInsertLogUser

CREATE TABLE StudentInsertLogUser
( UserName nchar(10),DateAndTime datetime
);

创建触发器,记录用户名和操作时间

CREATE TRIGGER Student_Time
ON Student
AFTER
INSERT
AS declare @UserName    nchar(10)declare @DateTime    datetime--相当于定义变量,变量名前要加@select @UserName = system_userselect @DateTime = CONVERT(datetime,GETDATE(),120) --2018-04-11 16:33:10INSERT INTO StudentInsertLogUser(UserName,DateAndTime)VALUES (@UserName,@DateTime)

DECLARE定义变量
SELECT取系统的值
INSERT语句将值插入

测试:

INSERT
INTO  Student
VALUES ('201215998','小龙人','男',20,'MA');
SELECT * FROM Student;
SELECT * FROM StudentInsertLog;
SELECT * FROM StudentInsertLogUser;



这里统计的是整个Student表中有多少元组。

【例5.23】定义一个BEFORE行级触发器,为教师表定义完整性规划“教授的工资不能低于4000元,如果低于,自动改为4000元”

建表:

CREATE TABLE Teacher1
(Tno CHAR(10) PRIMARY KEY,
Tname CHAR(10) UNIQUE,
Sal INT,
Job CHAR(10)
);

标准SQL:

CREATE TRIGGER Insert_Or_Update_Sal
BEFORE INSERT OR UPDATE ON Teacher1
--触发事件为插入或更新
FOR EACH ROW  --行级触发器
BEGINIF(new.Sal < 4000) AND (new.Job = '教授')THEN new.Sal=4000;END IF;
END;

触发动作体为BEGIN…END块,里面相当于C语言,IF…END IF相当于IF语句,满足条件执行THEN语句。这里没有REFERENCING语句,new就是新元组。

T-SQL:

CREATE TRIGGER Insert_Or_Update_Sal
ON Teacher1
FOR INSERT,UPDATE
ASdeclare @number CHAR(10)declare @name CHAR(10)declare @salary INTdeclare @job CHAR(10)select @salary = Sal FROM insertedselect @number = Tno FROM insertedselect @name = Tname FROM insertedselect @job = Job FROM inserted--更新或者插入,改变的新行都会存入到INSERTED表中IF(@salary < 4000 AND @job='教授')
BEGINIF EXISTS(SELECT * FROM Teacher1 WHERE Tno = @number)UPDATE Teacher1 SET Sal = 4000 WHERE Tno = @number--如果之前有数据,更新工资IF NOT EXISTS(SELECT * FROM Teacher1 WHERE Tno = @number)BEGININSERT INTO Teacher1VALUES(@number,@name,4000,@job)--否则插入数据END
ENDINSERT INTO Teacher1 VALUES('001','王晓易',3500,'教授');
INSERT INTO Teacher1 VALUES('002','张甜甜',2000,'教师');
INSERT INTO Teacher1 VALUES('003','李小李',6000,'教授');SELECT * FROM Teacher1;UPDATE Teacher1
SET Sal= 3000
WHERE Tno = '003';SELECT * FROM Teacher1;

执行插入语句后:

执行更新语句后:

T-SQL用FOR和AFTER。
DML 触发器语句使用两种特殊的表:删除的表deleted和插入的表inserted。

inserted表,插入表,存放插入的元组,是新行的副本。
deleted表,删除表,存放被删除的元组。
(以上内容摘自T-SQL官方文档)

激活触发器

触发器由触发事件激活,并由数据库服务器自动执行
含有多个触发器的执行顺序:
(1)BEFORE级触发器
(2)激活触发器上的SQL语句
(3)AFTER级触发器

删除触发器

DROP TRIGGER <触发器名> ON <表名>;

存储过程和函数

1.存储过程 —— PROCEDURE

由过程化SQL语句,经编译和优化后存储在数据库服务器中,可以被反复调用,运行速度较快。
相当于编写了一个函数库,方便调用。

创建存储过程:

CREATE OR REPLACE PROCEDURE 过程名([参数1,参数2,...])
AS <过程化SQL块>;

【例8.8】利用存储过程来实现下面的应用:从账户1转指定数额的款项到账户2中。

建立新表Account并插入两个账户:

CREATE TABLE Account
(
accountnum CHAR(3), -- 账户编号
total FLOAT     -- 账户余额
);INSERT INTO Account VALUES(101,50);
INSERT INTO Account VALUES(102,100);SELECT * FROM Account;

需要
1.检查账户1余额是否足够
2.账户1,2是否存在

标准SQL:

CREATE OR REPLACE PROCEDURE TRANSFER(inAccount INT,outAccount INT,amount FLOAT)--存储过程及其参数
AS DECLAREtotalDepositOut Float;totalDepositIn Float;inAccountnum INT; --定义变量
BEGIN--检查转出账户的余额SELECT total INTO totalDepositOut FROM AccountWHERE Accountnum = outAccount;--将Account表中的转出账户的余额赋给对应变量IF totalDepositOut IS NULLTHEN ROLLBACK;RETURNEND IF; --转出账户不存在,回滚IF totalDepositOut < amountTHENROLLBACK;RETURNEND IF; --账户余额不足,回滚
SELECT Accountnum INTO inAccountnum FROM Account
WHERE Accountnum = inAccount;IF inAccount IS NULL THENROLLBACK;RETURN
END IF;UPDATE Account SET total=total-amountWHERE accountnum = outAccount; --转出
UPDATE Account SET total = total + amountWHERE accountnum = inAccount; --转入
COMMIT;--提交转账事务
END;

T-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/*定义存储过程TRANSFER,参数为转入账户、转出账户、转账额度*/
AS
BEGIN TRANSACTION TRANS   DECLARE       /*定义变量*/@totalDepositOut Float,@totalDepositIn Float,@inAccountnum INT;/*检查转出账户的余额 */     SELECT @totalDepositOut = total FROM Account WHERE accountnum = @outAccount;/*如果转出账户不存在或账户中没有存款*/IF @totalDepositOut IS NULL                     BEGINPRINT '转出账户不存在或账户中没有存款'ROLLBACK TRANSACTION TRANS;       /*回滚事务*/RETURN;END;/*如果账户存款不足*/IF @totalDepositOut < @amount        BEGINPRINT '账户存款不足'ROLLBACK TRANSACTION TRANS; /*回滚事务*/RETURN;END/*检查转入账户的状态 */  SELECT @inAccountnum = accountnum  FROM Account    WHERE accountnum = @inAccount;/*如果转入账户不存在*/ IF @inAccountnum IS NULL                                BEGINPRINT '转入账户不存在'ROLLBACK TRANSACTION TRANS;/*回滚事务*/RETURN;END;/*如果条件都没有异常,开始转账。*/ BEGINUPDATE Account SET total = total - @amount WHERE   accountnum = @outAccount; /* 修改转出账户余额,减去转出额 */UPDATE Account SET total = total + @amount    WHERE   accountnum = @inAccount; /* 修改转入账户余额,增加转入额 */PRINT '转账完成,请取走银行卡'COMMIT TRANSACTION TRANS;                         /* 提交转账事务 */RETURN;END

变量要加@。

执行测试:
1.转入转出都存在的情况下,且余额充足:

EXEC Proc_TRANSFER@inAccount = 101,    --转入账户@outAccount = 102,  --转出账户@amount = 50        --转出金额SELECT * FROM Account


2.转入或转出账户不存在:

余额不会发生变化:

3.余额不足:

执行存储过程:

CALL/PERFORM PROCEDURE 过程名([参数1,参数2,...]);

类似于函数的调用,过程体中还可以调用其他存储过程,类似于函数的嵌套。

【例8.9】从账户01003815868转10000元到01003813828账户中。

标准SQL:

CALL PROCEDURE TRANSFER(01003813828,01003815868,10000)

T-SQL:

--先添加账户
INSERT INTO Account VALUES('01003815868','20000');
INSERT INTO Account VALUES('01003813828','1500');
EXEC Proc_TRANSFER@inAccount = 01003813828,@outAccount = 01003815868,@amount = 10000


标准SQL用CALL/PERFORM
T-SQL用EXEC,见上。

修改存储过程:

ALTER PROCEDURE 过程名1 RENAME TO 过程名2;

删除存储过程:

DROP PROCEDURE 过程名();

2.函数

函数和存储过程的异同:
同——都是持久性存储模块
异——函数必须指定返回值类型

函数定义:

CREATE OR REPLACE FUNCTION 函数名([参数1,参数2,...])
RETURNS <类型>
AS <过程化SQL块>;

比存储过程就多了一个RETURNS返回类型

函数执行:

CALL/SELECT 函数名([参数...])

修改函数:

--重命名
ALTER FUNCTION 函数名1 RENAME TO 函数名2
--重新编译
ALTER FUNCTION 函数名 COMPILE

【心得】
用T-SQL和标准SQL写触发器的时候,区别挺大,参照老师给出的【例5.22】,自己尝试写了【例5.23】的T-SQL版本,经过多次修改和查资料后运行成功,费时较多。写到后面,触发器和存储过程竟然有些混。

完整性+存储过程和函数——CHECK / CONSTRAINT / TRIGGER / PROCEDURE/ FUNCTION相关推荐

  1. 【Hbu数据库】第七周 数据库完整性 存储过程和函数

    数据库完整性 实体完整性 定义实体完整性 实例完整性检查和违约处理 参照完整性 定义参照完整性 参照完整性检查和违约处理 用户定义完整性 属性上的约束条件 属性上约束条件的定义 元组上的约束条件 完整 ...

  2. MySQL(视图、存储过程与函数、流程控制、触发器)

    第一章 视图 1 什么是视图 为了提高复杂的SQL语句的复用性和表的操作的安全性 ,MySQL数据库管理系统提高了视图特性,所谓视图,本质上是一种虚拟表,其内容与真实的表相似,包含一系列带有名称的列和 ...

  3. MySQL中引入存储引擎意义是_mysql学习九:存储引擎、存储过程和函数的引入

    存储引擎: 存储引擎是mysql特有的,共有7种,常用的有myisam.memory.innodb 查看表的存储引擎: show create table 表名; 修改表的存储引擎: alter ta ...

  4. 自定义存储过程和函数

    MYSQL的存储过程和函数 MYSQL中创建存储过程和函数分别使用CREATE PROCEDURE和CREATE FUNCTION 使用CALL语句来调用存储过程,存储过程也可以调用其他存储过程 函数 ...

  5. mySQL 教程 第7章 存储过程和函数

    存储过程和存储函数 MySQL的存储过程(stored procedure)和函数(stored function)统称为stored routines. 1. MySQL存储过程和函数的区别 函数只 ...

  6. mysql 存储过程与函数_12 MySQL存储过程与函数

    存储过程和函数 存储过程和函数是事先经过编译并存储在数据库中的一段SQL语句的集合. 调用存储过程和函数可以简化应用开发人员的工作,减少数据在数据库和应用服务器之间的传输,提高数据处理的效率. 存储过 ...

  7. 存储过程同函数的区别

          前两天面试时被面试官问到这个问题,搜肠刮肚也没有说出个所以然来.看来对数据库知识还是要时时总结归纳一下啊! 在网上找了些资料,归纳如下: 先上个图片: Oracle中差别 SQL SERV ...

  8. mySQL教程 第7章 存储过程和函数

    第7章 存储过程和函数 存储过程和存储函数 MySQL的存储过程(stored procedure)和函数(stored function)统称为stored routines. 1. MySQL存储 ...

  9. SQL存储过程和函数

    SQL存储过程: 由来:在具体应用中,一个完整的操作会包含多条SQL语句,在执行过程中需要根据前面SQL语句的执行结果有选择的执行后面的SQL语句.因此,mysql提供了数据库对象存储过程和函数. 定 ...

最新文章

  1. 使用阿里云容器服务Kubernetes实现蓝绿发布功能
  2. 比nginx-rtmp高三倍性能的SRS的高性能是个什么球?
  3. dict python用法_Python_Dict用法梳理
  4. JDK源码包结构分类
  5. 网络协议,各层功能,各层协议
  6. Java一只青蛙每次跳3米_行测丨青蛙跳5米下滑3米?怎么办!
  7. 我们用计算机做什么大餐英语怎么说,感恩用英语怎么说
  8. 深度学习之RNN、LSTM、GRU篇
  9. 2021-2025年中国伊维菌素原料药行业市场供需与战略研究报告
  10. CentOS7入门:使用Vi文本编辑器
  11. 神仙打架!Python web框架大比拼,哪个学习成本低?
  12. sql注入搞事情(连载一)
  13. Java - Set 接口
  14. LATEX关于表格的一些处理
  15. python cv2什么意思_cv2,CV2是什么意思
  16. python中\t \r \s \n \f各种转移字符含义
  17. DOA算法2:ESPRIT算法
  18. 如何初始window的注册表,恢复默认设置
  19. 解决Error:initialization failed for ‘https://start.spring.io‘‘ please check URL
  20. lvds单8转双8芯片_液晶驱动板中的 LVDS 单6 单8 双6 双8 指的是什么

热门文章

  1. 舍弗勒城市车辆转向系统研究完成,可实现全自动平行泊车
  2. Zilliqa 的设计构思 第3部分:使共识更有效
  3. HTML+CSS制作DNA双螺旋结构
  4. Display port
  5. 基于单片机智能安全服药提醒药盒系统设计(毕设资料)
  6. 悬挂式数字麦克风阵列 多点视频会议
  7. Springboot使用Specification连表查询LEFT
  8. 资深猎头眼中的优质简历是如何炼成的?
  9. h5小游戏在线玩:万圣节互动游戏《逃离禁闭岛》通关完全攻略
  10. vCenter6.7 取消检查更新通知