Experiment6
3 完整性语言实验
3.1 实体完整性实验
3.1.1 实验目的
掌握实体完整性的定义和维护方法。
3.1.2 实验内容和要求
定义实体完整性,删除实体完整性。能够写出两种方式定义实体完整性的 SQL 语句:创建表时定义实体完整性、创建表后定义实体完整性。设计 SQL 语句验证完整性约束是否起作用。
3.1.3 实验重点和难点
实验重点:创建表时定义实体完整性。
实验难点:有多个候选码时实体完整性的定义。
3.1.4 实验内容记录
这里参照 employees 数据库创建一个新的数据库 test;
CREATE DATABASE test;
3.1.4.1 创建表时定义实体完整性(列级实体完整性)
创建雇员表。
1 | CREATE TABLE employees ( |
3.1.4.2 创建表时定义实体完整性(表级实体完整性)
创建雇员表。
1 | CREATE TABLE employees ( |
注:MYSQL 不支持约束命名。
3.1.4.3 创建表后定义实体完整性
创建雇员表。
1 | CREATE TABLE employees ( |
3.1.4.4 定义实体完整性(多属性主码)
创建职位表。
1 | CREATE TABLE titles ( |
3.1.4.5 有多个候选码时定义实体完整性
当存在多个候选码时,只能定义一个主码,其它的候选码定义唯一性约束。
创建部门表。
1 | CREATE TABLE departments ( |
3.1.4.6 删除实体完整性
删除部门表上的实体完整性。
1 | ALTER TABLE departments |
3.1.5 思考
- 所有列级完整性约束都可以改写为表级完整性约束,而表级完整性约束不一定能改写为列级完整性约束。请举例说明。
例如多属性主码就只能通过表级完整性约束定义。列级完整性约束可以实现缺省约束和非空约束,而表级完整性约束无法实现。表级完整性约束只能实现主键约束、外键约束、唯一性约束、检查约束
表级完整性约束不一定能改写为列级完整性约束,因为有些表级完整性约束涉及到多个列,而列级完整性约束只能应用于单个列。例如,以下语句使用了表级完整性约束:
1 | CREATE TABLE project_assignments ( |
不能改写为以下语句,因为 PRIMARY KEY 约束不能作为列级完整性约束应用于多个列:
1 | CREATE TABLE project_assignments ( |
- 什么情况下会违反实体完整性约束,DBMS 将做何种违约处理?请用实验验证。
当更新数据时,新的数据可能违反实体完整性约束。此时 DBMS 会拒绝执行。
举例:插入的数据中,对应的主码为空值时,就会违反实体完整性约束dept_no
是主码,所以不能为空
3.2 参照完整性实验
3.2.1 实验目的
掌握参照完整性的定义和维护方法。
3.2.2 实验内容和要求
定义参照完整性,定义参照完整性的违约处理,删除参照完整性。写出两种方式定义参照完整性的 SQL 语句:创建表时定义参照完整性、创建表后定义参照完整性。
3.2.3 实验重点和难点
实验重点:创建表时定义参照完整性。
实验难点:参照完整性的违约处理定义。
3.2.4 实验内容记录
3.2.4.1 创建表时定义参照完整性
在已有员工表的情况下,定义工资表。
列级参照完整性定义。
1 | CREATE TABLE salaries ( |
注:经测试,MYSQL 定义列级外键约束无效。
表级参照完整性定义。
注:MYSQL 支持外键约束命名。
1 | CREATE TABLE salaries ( |
3.2.4.2 创建表后定义参照完整性
1 | CREATE TABLE salaries ( |
3.2.4.3 定义参照完整性的违约处理
定义两张表:
1 | CREATE TABLE departments ( |
注:如果要定义删除后的行为,则必须不能与已有的限制冲突(如不能定义 NOT NULL)。
插入两条数据:
1 | INSERT INTO departments |
然后删除被参照表中的元组,之后参照表会变成:
1 | +--------+---------+ |
3.2.4.4 删除参照完整性
删除 emp_dept 上的外码。
注:和书中写法稍有不同。
ALTER TABLE emp_dept DROP FOREIGN KEY FK_deptno;
3.2.5 思考
- 对于自引用表,例如课程表(课程号、课程名、先修课程号、学分)中的先修课程号引用该表的课程号,请完成如下任务:
(1)写出课程表上的实体完整性和参照完整性。
(2)在考虑实体完整性约束的条件下,试举出几种录入课程数据的方法。在录入数据时,可以按照引用的顺序录入数据(总是先录入不存在引用或引用已存在的数据),也可以临时移除完整性约束,在录入数据后,再添加完整性约束。1
2
3
4
5
6
7
8
9
10
11CREATE TABLE course (
cno CHAR(4),
cname VARCHAR(40),
cpno CHAR(4),
ccredit SMALLINT
);
ALTER TABLE course
ADD PRIMARY KEY(cno);
ALTER TABLE course
ADD CONSTRAINT FK_cpno
FOREIGN KEY(cpno) REFERENCES course(cno);
如果数据本身无法满足约束,则引入约束时会失败。
比如先加入数据:这一条数据存在依赖问题,此时再引入完整性约束,则会出现无法引入的情况。1
2INSERT INTO course
VALUES('0001','DataStructure','0002',80);
3.3 用户自定义完整性实验
3.3.1 实验目的
掌握用户自定义完整性的定义和维护方法。
3.3.2 实验内容和要求
针对具体应用语义,选择 NULL、NOT NULL、DEFAULT、DEFAULT、UNIQUE、CHECK 等,定义属性上的约束条件。
3.3.3 实验重点和难点
实验重点:NULL、NOT NULL、DEFAULT
实验难点:CHECK
3.3.4 实验内容记录
3.3.4.1 定义属性 NULL、NOT NULL 约束
注:默认约束即为 NULL 约束,即可以为 NULL 。
这里不作示例。
3.3.4.2 定义属性 DEFAULT 约束
DEFAULT 约束用于给定属性的默认值,即不提供值的时候自动填充的值。
这里不作示例。
3.3.4.3 定义 UNIQUE 约束
UNIQUE 约束即必须唯一。
3.3.4.4 使用 CHECK 约束条件
CHECK 可以跟表达式,并且可以引用多个属性。
如:
1 | ... |
4 触发器实验
4.1 触发器实验
4.1 实验目的
掌握数据库触发器的设计和使用方法。
4.2 实验内容和要求
定义 BEFORE 触发器和 AFTER 触发器。能够理解不同类型触发器的作用和执行原理,验证触发器的有效性。
4.3 实验重点和难点
实验重点:触发器的定义。
实验难点:利用触发器实现较为复杂的用户自定义完整性。
4.4 实验内容记录
4.4.1 AFTER 触发器
AFTER 触发器可以用来维护一致性。
比如有一张员工总工资表,那么在加入新的工资记录后,需要更新总工资表。
总工资表:
1 | CREATE TABLE emp_salary_total ( |
建立总工资表后,初始化一次数据:
1 | INSERT INTO emp_salary_total |
在总工资表上定义触发器:
1 | DELIMITER $$ |
下面进行验证:
先查询 10001 的总工资:
1 | SELECT * |
得到总工资为:
1 | +--------+--------------+ |
从中删除一条数据:
DELETE FROM salaries WHERE salary=84917 AND emp_no=10001 $$
再查询总工资为:
1 | +--------+--------------+ |
符合预期。
在触发器上不应定义过于复杂(耗时)的动作,例如本例中触发器执行了全扫描更新。更好的处理方式是利用被删除的行的信息来增量更新总工资。
1 | DELIMITER $$ |
此时再删除一条数据,
DELETE FROM salaries WHERE salary=85112 AND emp_no=10001 $$
然后查询总工资为
1 | +--------+--------------+ |
同样符合预期。
4.4.2 BEFORE 触发器
BEFORE 触发器可以用来检查数据更新的合法性,可以用来实现比断言更为复杂的检查(断言只能定义一个 CHECK 子句)。
例如,加入一个日期检查,要求 to_date 必须大于 from_date 。
注:MYSQL 抛出异常的写法和书中给出的不同。
1 | DELIMITER $$ |
然后插入一条一场数据:
1 | INSERT INTO salaries |
会得到错误:
ERROR 1644 (HY000): to_date is EARLIER than from_date!