数据库系统概论 实验四 数据控制(完整性)

1实验目的

1.熟悉通过SQL对数据进行完整性控制。
2.完成书本上习题的上机练习。

2实验工具MYSQL

Mysql 命令行工具

3实验内容和要求

使用SQL对数据进行完整性控制(3类完整性、CHECK短语、CONSTRAIN子句、触发器)。用实验证实,当操作违反了完整性约束条件时,系统是如何处理的。根据以下要求认真填写实验报告,记录所有的实验用例。

4实验步骤

4.1三类完整性

4.1.1实体完整性

关系模式的实体完整性在create table中用primary key定义,
定义主码的方式分为定义为列级约束条件和定义表级约束条件两种。

(1)定义Student,并将其中的Sno属性定义为主码:
CREATE TABLE STUDENT(
SNO CHAR(9) PRIMARY KEY,
SNAME CHAR(8) NOT NULL,
SSEX CHAR(2),
SAGE SMALLINT,
SDEPT CHAR(20));

或者:

CREATE TABLE STUDENT(
SNO CHAR(9),
SNAME CHAR(8) NOT NULL,
SSEX CHAR(2),
SAGE SMALLINT,
SDEPT CHAR(20),
PRIMARY KEY(SNO)
);

创建成功后,查看student的结构如下:

验证参照性:
使用插入语句,插入已有的学号,按照主码的实体完整性约束,应该插入失败:

INSERT INTO STUDENT VALUES(‘201215121’,’李勇’,’男’,’20’,’CS’);

验证成功。

(2)定义表SC,将其中的属性Sno,Cno定义为主码:
CREATE TABLE SC(
SNO CHAR(9) NOT NULL,
CNO CHAR(4) NOT NULL,
GRADE SMALLINT,
PRIMARY KEY(SNO,CNO));

创建成功后,查看表的结构如下:

验证:

insert into SC values(‘201215122’,’2’,’90’);

4.1.2参照完整性

关系模型的参照完整性是再create table中用foreign key语句来定义的,并用references来知名外码参照宝的是哪些表的主码。

定义表SC,其中Sno参照表Student的主码Sno,Cno参照表Sourse的主码Cno。

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

创建成功后,表的结构如下:

分析:好像没用。Extra一列什么东西都没有。其实extra表示其他信息,和外码无关。
用[show create table sc;] 命令查看该表的所有结构,确实显示外键创建成功:

验证:
插入一条学号不属于student表的信息:

insert into SC values(‘201115122’,’2’,’90’);

验证成功,显示:
Cannot add or update a child row: a foreign key constraint fails (lab.sc, CONSTRAINT sc_ibfk_1 FOREIGN KEY (SNO) REFERENCES student (Sno))

4.1.3用户自定义完整性

在create table中定义属性的同时,可以根据应用要求定义属性上的约束条件,即属性值约束,包括:

  • 列值非空(not null)
  • 列值唯一(unique)
  • 检查列值是否满足一个条件表达式(check短语)
(1)不允许取空值:

定义SC表时,Sno、Cno和Grade属性都不允许取空值,在不特别声明的情况下,非码属性的值是允许取空值的。

CREATE TABLE SC(
SNO CHAR(9) NOT NULL,
CNO CHAR(4) NOT NULL,
GRADE SMALLINT NOT NULL,
);

懒得删除重建,直接使用alter修改约束条件:

ALTER TABLE SC
MODIFY GRADE SMALLINT NOT NULL;

结果如下:

验证:

INSERT INTO SC VALUES(‘201215122’,’2’,NULL);

(2)列值唯一:

建立部门表DEPT,要求部门名称Dname列值唯一,部门编号Deptno列为主码。

CREATE TABLE DEPT(
DEPTNO NUMERIC(2),
DNAME CHAR(9) UNIQUE NOT NULL,
LOCATION CHAR(10),
PRIMARY KEY(DEPTNO)
);

结果如下:

验证:

INSERT INTO DEPT VALUES(2, “开发”, “北京”);
INSERT INTO DEPT VALUES(3, “开发”, “上海”);

(3)用check短语指定列值应该满足的条件

见4.2。

4.2CHECK短语

4.2.1属性上的约束条件

用CHECK短语可以指定列值应该满足的条件。
但Mysql目前版本不支持SQL标准的检查约束(CHECK),虽然可以设置,但只是个摆设,没有任何的作用。

(1)student表的ssex只允许取“男”或者“女”
CREATE TABLE STUDENT(
SNO CHAR(9),
SNAME CHAR(8) NOT NULL,
SSEX CHAR(2) CHECK (SSEX IN (“男”,”女”)),
SAGE SMALLINT,
SDEPT CHAR(20),
PRIMARY KEY(SNO)
);
(2)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 COURSE(CNO)
);

验证:
插入成绩大于100的:

INSERT INTO SC VALUES(‘201215122’,’2’,200);

插入成功,验证失败。

确实如官方手册所言,mysql能只支持check的分析而已。

4.2.2元组上的约束条件

【例】当学生的性别是男时,其名字不能以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.%’)
);

4.3CONSTRAIN子句

SQL还在create table中提供了完整性约束命名子句constraint,用来对完整性约束条件命名,从而可以灵活地增加、删除一个完整性约束条件。

(1)建立学生登记表student,要求学号在90000~99999之间,姓名不能取空值,年龄小于30,性别只能是“男”或“女”。
CREATE TABLE STUDENT1(
SNO NUMERIC(6),
CONSTRAINT C1 CHECK (SNO BETWEEN 90000 AND 99999),
SNAME CHAR(20),
/*CONSTRAINT C2 SNAME NOT NULL ,*/
SAGE NUMERIC(3),
CONSTRAINT C3 CHECK (SAGE < 30),
SSEX CHAR(2),
CONSTRAINT C4 CHECK (SSEX IN (“男”,”女”)),
CONSTRAINT STUDENTKEY PRIMARY KEY(SNO)
);
(2)建立教师表TEACHER,要求每个教师的英法工资不低于3000元。应发工资是工资列sal和扣除项deduct之和。
CREATE TABLE TEACHER(
ENO NUMERIC(4) PRIMARY KEY,
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)
);

4.4触发器

(参考链接)[https://www.cnblogs.com/fengxw/p/6076150.html]
触发器又叫做事件-条件-动作规则,当特定的系统时间发生时,对规则的条件进行检查,如果条件成立则执行规则中的动作,否则不能执行。
可以建立6种触发器,即:BEFORE INSERT、BEFORE UPDATE、BEFORE DELETE、AFTER INSERT、AFTER UPDATE、AFTER DELETE。

另外有一个限制是不能同时在一个表上建立2个相同类型的触发器,因此在一个表上最多建立6个触发器。
触发器分为三类,分别是更新触发器、插入触发器和删除触发器。

  • INSERT 型触发器:插入某一行时激活触发器,可能通过 INSERT、LOAD DATA、REPLACE 语句触发;
  • UPDATE 型触发器:更改某一行时激活触发器,可能通过 UPDATE 语句触发;
  • DELETE 型触发器:删除某一行时激活触发器,可能通过 DELETE、REPLACE 语句触发。

Mysql中触发器的语法如下:

CREATE TRIGGER trigger_name trigger_time trigger_event ON tb_name FOR EACH ROW trigger_stmt
trigger_name:触发器的名称
tirgger_time:触发时机,为BEFORE或者AFTER
trigger_event:触发事件,为INSERTDELETE或者UPDATE
tb_name:表示建立触发器的表明,就是在哪张表上建立触发器
trigger_stmt:触发器的程序体,可以是一条SQL语句或者是用BEGINEND包含的多条语句
(1)当对表SC的GRADE进行修改时,若分数增加了10%,则将此次操作记录到另一个表SC_U(Sno,Cno,Oldgrade,Newgrade)中。

先创建SC_U表:

CREATE TABLE SC_U(
SNO CHAR(9) NOT NULL,
CNO CHAR(4) NOT NULL,
OLDGRADE SMALLINT,
NEWGRADE SMALLINT
);

再创建触发器:
出现问题,语句中存在几个分号,SQL会提前结束命令,出错。需要将结束的指令暂时用DELIMITER 命令,将其改为不冲突的字符。
譬如:

    DELIMITER *
    CREATE TRIGGER SC_T
    AFTER UPDATE OF GRADE ON SC
    REFERENCING
        OLDROW AS OLDTUPLE,
        NEWROW AS NEWTUPLE
    FOR EACH ROW
    WHEN (NEWTUPLE.GRADE >= 1.1 * OLDTUPLE.GRADE)
        INSERT INTO SC_U        
        VALUES(OLDTUPLE.SNO, OLD.CNO,OLD.GRADE,NEW.GRADE)
    *

报错:大概是因为mysql不存在referencing。并且使用new,old来标志新的数据和旧的数据。

所以修改为:

DELIMITER $
CREATE TRIGGER SC_T
AFTER UPDATE ON SC
FOR EACH ROW
BEGIN
IF (NEW.GRADE >= 1.1 * OLD.GRADE) THEN
INSERT INTO SC_U 
VALUES(OLD.SNO, OLD.CNO,OLD.GRADE,NEW.GRADE);
END IF;
END
$

(2)定义一个BEFORE行级触发器,为教师表Teacher定义完整性规则“教授的工资不得低于4000元,如果低于4000元,自动改为4000元”

DELIMITER *
CREATE TRIGGER INSERT_OR_UPDATE_SAL
BEFORE INSERT ON TEACHER
FOR EACH ROW
BEGIN
IF(NEW.JOB=”教授”) AND (NEW.SAL < 4000)
THEN 
SET NEW.SAL = 4000;
END IF;
END
*

验证:

INSERT INTO TEACHER VALUES(1,”教授”,3999,0,1)*

结果更新成功。

5实验中的问题

(1)编码格式问题

mysql> INSERT INTO DEPT VALUES(‘1’,’FFF团,’北京’);
ERROR 1054 (42S22): Unknown column ‘‘1’’ in ‘field list’

编码格式错误。

仔细对照了一下,貌似是标点符号的问题,虽然使用的同样是英文的引号,但是有区别。

char型的变量用双引号括起来之后就好了。

(2)constraint 语句出错,mysql的用法有些许的不同。

constraint子句需要用逗号隔开,并且mysql中constraint好像不能使用非空约束。于是注释掉not nul一行后,创建成功。

(参考链接)[https://blog.csdn.net/xuntianzong/article/details/19539639]

(3)创建触发器时,由于语句中分号,提前结束了。

tips:一般情况下,mysql默认是以 ; 作为结束执行语句,与触发器中需要的分行起冲突
   为解决此问题可用DELIMITER,如:DELIMITER ||,可以将结束符号变成||
   当触发器创建完成后,可以用DELIMITER ;来将结束符号变成;
mysql> DELIMITER ||
mysql> CREATE TRIGGER demo BEFORE DELETE
-> ON users FOR EACH ROW
-> BEGIN
-> INSERT INTO logs VALUES(NOW());
-> INSERT INTO logs VALUES(NOW());
-> END
-> ||

(4)语法错误

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ” at line 8

解决:语法错误,不是少符号就是多符号。这里是缺少了END IF;

(5)

INSERT类的触发器不存在OLD。

6实验总结

通过该试验进一步熟悉了数据库的脚本语言,加深了对数据库的认识。Mysql很多地方与教材上的不一样,所以会有很多坑。

猜你喜欢

转载自blog.csdn.net/qq_30501975/article/details/80314145