数据库原理实验五——触发器与存储过程(函数)

实验目的

  1. 熟练掌握存储过程/函数的创建和执行方法。
  2. 熟练掌握触发器的创建和执行方法。

实验内容

  1. 针对SPJ_MNG数据库,创建并执行如下存储过程。(共计40分)

(1) 创建一个没有参数的存储过程—jsearch1。该存储过程的作用是:当执行该存储过程时,将返回S表中北京供应商的所有信息。调用该存储过程并验证结果。(5分)

DELIMITER $$
CREATE PROCEDURE jsearch1()
BEGIN
	SELECT * 
    FROM S
    WHERE CITY = '北京';
END $$

验证

CALL jsearch1;

(2) 创建带输入参数的存储过程—jsearch2。该存储过程的作用是:当输入一个供应商所在城市名时(如北京),将返回该供应商的所有信息。调用存储过程并验证结果。(5分)

DELIMITER $$
CREATE PROCEDURE jsearch2(in city_in CHAR(45))
BEGIN
	SELECT * 
    FROM S
    WHERE CITY = city_in;
END $$

验证

CALL jsearch2('北京');

(3) 创建带输入参数和输出参数的存储过程(函数)—jsearch3。该存储过程的作用是:当输入一个供应商编号(输入参数SNO)时,将返回该供应商的名称(输出参数SNAME)。调用存储过程并验证结果。(5分)

DELIMITER $$
CREATE PROCEDURE jsearch3(in sno_in CHAR(45),out sname_out CHAR(45))
BEGIN
	SELECT SNAME
    INTO sname_out
    FROM S
    WHERE SNO = sno_in;
END $$

验证

CALL jsearch3('S1',@name);
SELECT @name;

(4) 创建一个使用游标的存储过程jsearch4,创建成功后调用该存储过程并验证结果。该存储过程的功能:当输入一个工程号JNO时,将返回供应该工程零件的所有供应商的名称(SNAME),这些供应商名拼接成一个字符串,并用逗号’,’分隔。

DELIMITER $$
CREATE PROCEDURE jsearch4(in jno_in CHAR(45))
BEGIN
	DECLARE TEMP CHAR(100) DEFAULT '''';
    DECLARE NAME CHAR(10);
    DECLARE DONE INT DEFAULT 0;
	DECLARE C_NAME CURSOR FOR
		SELECT DISTINCT S.SNAME
		FROM S,SPJ
		WHERE S.SNO = SPJ.SNO
		AND SPJ.JNO = jno_in;
	DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET DONE = 1;
    OPEN C_NAME;
    FETCH C_NAME INTO NAME;
    REPEAT
		SET TEMP =CONCAT(TEMP,NAME);
        SET TEMP =CONCAT(TEMP,',');
        FETCH C_NAME INTO NAME;
    UNTIL DONE
    END REPEAT;
    SET TEMP =CONCAT(TEMP,'''');
    CLOSE C_NAME;
    SELECT TEMP;
END $$

验证

CALL jsearch4('J2');

(5) 查看存储过程jsearch1和jsearch2的文本信息。(5分)

show create procedure jsearch1 \G;
show create procedure jsearch2 \G;

(6) 查看存储过程jsearch1基本状态信息。(5分)

show procedure status like 'jsearch1';

(7) 删除jsearch1存储过程。(5分)

DROP PROCEDURE jsearch1;
  1. 针对Student数据库,创建和执行如下的触发器:(共计40分)

(1) 删除SC表上的外键约束,针对SC表创建一个名为insert_s的INSERT触发器。该触发器的功能:当用户向SC表中插入记录时,如果插入的cno值不是C表中Cno的已有值,则提示用户“不能插入C表中没有的数据”,并阻止该数据的插入;如果插入的sno值不是S表中的sno的已有值,则提示用户“不能插入S表中没有的数据”,并阻止该数据的插入。触发器创建成功之后,向SC表插入记录,验证触发器是否正常工作。(5分)

DELIMITER $$
CREATE TRIGGER insert_s BEFORE INSERT ON SC FOR EACH ROW
BEGIN
	IF NEW.SNO NOT IN (SELECT DISTINCT(SNO) FROM S)
		THEN SIGNAL SQLSTATE '45000'
		SET MESSAGE_TEXT = '不能插入S表没有的数据';
	END IF;
    IF NEW.CNO NOT IN (SELECT DISTINCT(CNO) FROM S)
		THEN SIGNAL SQLSTATE '45000'
		SET MESSAGE_TEXT = '不能插入C表没有的数据';
	END IF;
END $$

验证

INSERT 
INTO SC(ID,SNO,CNO)
VALUES(10000,'100001','5');

INSERT 
INTO SC(ID,SNO,CNO)
VALUES(10000,'100','7');

(2) 为S表创建一个名为dele_s1的DELETE触发器,该触发器的作用是提示用户“不能删除该表中的数据”并阻止用户删除S表中的数据。触发器创建成功之后,删除S表中记录,验证触发器是否正常工作。(5分)

DELIMITER $$
CREATE TRIGGER dele_s1 BEFORE DELETE ON S FOR EACH ROW
BEGIN
	SIGNAL SQLSTATE '03100'
    SET MESSAGE_TEXT = '不能删除该表中的数据';
END $$

验证:

DELETE 
FROM s
WHERE SNO = '10001';

(3) 为S表创建一个名为dele_s2的DELETE触发器,该触发器的作用是删除S表中的记录时删除SC表中该学生的选课纪录。触发器创建成功之后,删除S表中的记录,验证触发器是否正常工作(确认S表和SC表相关数据是否被删除)。(5分)

DELIMITER $$
CREATE TRIGGER dele_s2 AFTER DELETE ON S FOR EACH ROW
BEGIN
	SET @SNO = OLD.SNO;
    DELETE FROM SC WHERE SNO = @SNO;
END $$

验证:

DELETE 
FROM S
WHERE SNO = '10001';

(4) 为S表创建一个名为update_s的UPDATE触发器,该触发器的作用是禁止更新S表中“sdept”字段的内容(更新不成功,并且提示“不能更新sdept字段”)。触发器创建成功之后,更新S表中“sdept”字段的内容,验证触发器是否正常工作。(5分)

DELIMITER $$
CREATE TRIGGER update_s BEFORE UPDATE ON S FOR EACH ROW
BEGIN
	IF NEW.SDEPT != OLD.SDEPT
    THEN 
		SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = '不能更新sdept字段';
	END IF;
END $$

验证:

UPDATE S
SET SDEPT = 'CS'
WHERE SNO = '1';

(5) 删除update_s触发器。(5分)

DROP TRIGGER update_s;

(6) 自己设计一个before update触发器和after update触发器,比较before 和after触发器的区别。(5分)
before触发器

DELIMITER $$
CREATE TRIGGER update_s_before BEFORE UPDATE ON S FOR EACH ROW
BEGIN
	IF NEW.SDEPT = 'CS'
    THEN
		SET NEW.SDEPT = 'IS';
    END IF;
		
END $$

after触发器

DELIMITER $$
CREATE TRIGGER update_s_after after UPDATE ON S FOR EACH ROW
BEGIN
	IF NEW.SDEPT = 'CS'
    THEN
		SET NEW.SDEPT = 'IS';
    END IF;
		
END $$

after触发器在事务执行之后生效,上述代码中将学生表的系修改为IS,对于一个已经执行了update操作的事务,再将其修改回来系统会报错。故这种情况下的after触发器不能被创建。
验证:

UPDATE S
SET SDEPT = 'CS'
WHERE SNO = '1';

(7) 创建一个新的课程成绩统计表 CAvgGrade(Cno, Snum, examSNum, avgGrade),分别表示课号,选该课程的学生人数,参加考试人数,该门课程的平均成绩。利用触发器实现如下的功能:当SC表中插入、删除或者更新某个人的成绩时,自动更新表CAvgGrade。注意SC表中的grade为NULL时表明该学生还未参加考试,计算平均成绩时不需要计算该成绩,但是grade为0即考试成绩为0时,计算平均成绩需要计算该学生成绩。(10分)

首先设计一个公共的存储过程,这个存储过程可供三个触发器调用,实现数据的插入CAvgGrade表中。

DELIMITER $$
CREATE  PROCEDURE update_cavggrade(in in_cno INT)
BEGIN
	DECLARE e_snum INT;
	DECLARE e_examsnum INT;
	DECLARE e_avggrade INT;
	SELECT COUNT(*) FROM sc WHERE cno = in_cno
	INTO e_snum;
	SELECT count(*) FROM sc WHERE cno = in_cno AND grade is not null
	INTO e_examsnum;
	SELECT AVG(grade) FROM sc WHERE cno = in_cno AND grade is not null
	INTO e_avggrade;
	IF in_cno IN (SELECT DISTINCT cno FROM cavggrade)
		THEN UPDATE cavggrade SET Snum = e_snum,examSNum = e_examsnum,avgGrade = e_avggrade
		WHERE cno = in_cno;
	ELSE 
		INSERT INTO cavggrade VALUES(in_cno,e_snum,e_examsnum,e_avggrade);
	end IF;
END$$

插入触发器:

DELIMITER $$
CREATE TRIGGER insert_sc AFTER INSERT ON SC FOR EACH ROW
BEGIN
	call update_cavggrade(new.cno);
END$$

更新触发器:

DELIMITER $$
CREATE TRIGGER update_sc AFTER UPDATE ON SC FOR EACH ROW
BEGIN
	call update_cavggrade(new.cno);
    call update_cavggrade(old.cno);
END$$

删除触发器

DELIMITER $$
CREATE TRIGGER delete_sc AFTER DELETE ON SC FOR EACH ROW
BEGIN
    call update_cavggrade(old.cno);
END$
  1. 创建一个员工表employee(eID, eName, salary),假设该表中有1000条员工数据,完成下列要求。(总计20分,每题10分)

(1) 为了协助本题自动生成1000条员工数据,创建一个自动生成员工ID的用户自定义函数generateEID。其中员工ID要求是一个8位的数字,前四位表示插入员工数据的当前年份,后四位按照从0001到9999的顺序增长。例如2015年插入的第一条数据是20150001,所有1000条员工ID分别是20150001-20151000。调用该函数实现自动插入1000条数据。(注意插入数据的时候员工姓名可以为任意值,工资是2000-5000之间的数字)

DELIMITER $$
CREATE  PROCEDURE generateEID(in n int)
BEGIN
	SET @x = 0;
	REPEAT 
		SET @x = @x + 1;
		INSERT INTO employee VALUES(20210000 + @x,concat('ename',cast(@x as char(10))),floor(2000 + 3000*rand()));
	UNTIL @x >= n
	END REPEAT;
END$$

执行

1.	call generateEID(1000); 

(2) 该公司计划为员工按照一定的规则涨工资,请使用游标创建一个存储过程,执行该存储过程完成本次工资调整。工资增长规则如下:

  • 工资在3000元以下,每月涨300元;
  • 工资在3000-4000元之间,每月涨200元;
  • 工资大于或者等于4000元,每月涨50元;
DELIMITER $$
		CREATE PROCEDURE salary_increase ()
		BEGIN
			DECLARE e_salary int;
			DECLARE e_eid int;
  		  	DECLARE done int;
			DECLARE c_name cursor for 
				SELECT eID,salary from employee;
			DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1; 
    		OPEN c_name;
			FETCH c_name INTO e_eid,e_salary;  
			REPEAT
				CASE 
        		WHEN e_salary < 3000
					THEN UPDATE employee SET salary = salary + 300 WHERE eid = e_eid;
				WHEN e_salary >= 3000 and e_salary < 4000
					THEN UPDATE employee SET salary = salary + 200 where eid = e_eid;
				ELSE	
					UPDATE employee SET salary = salary + 50 where eid = e_eid;
				END CASE;	
				FETCH c_name INTO e_eid,e_salary;
			UNTIL done
			END REPEAT;	
			CLOSE c_name;		
END$$

执行

call salary_increase();
  • 5
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alfred young

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值