实验六 存储过程实验
实验6.1 存储过程实验
1.实验目的
掌握数据库PL/SQL编程语言,以及数据库存储过程的设计和使用方法。
2.实验内容和要求
存储过程定义,存储过程运行,存储过程更名,存储过程删除,存储过程的参数传递。掌握PL/SQL编程语言和编程规范,规范设计存储过程。
3.实验重点和难点
实验重点:存储过程定义和运行。
实验难点:存储过程的参数传递方法。
4.实验过程
(1)无参数的存储过程
定义一个存储过程,更新支出表中的教材总费用。
DELIMITER $
CREATE PROCEDURE proc_calTextBookExpense()
BEGIN
UPDATE expenditure
SET amount = (
SELECT SUM(total_price)
FROM textbook_order)
WHERE expenditure_type = "textbook";
END$
DELIMITER ;
执行存储过程。
CALL proc_calTextBookExpense();
(2)有参数的存储过程
定义一个存储过程,更新特定课程的容量。
DELIMITER $
CREATE PROCEDURE proc_updateCourseCapacity(cno CHAR(9), newCapacity SMALLINT)
BEGIN
UPDATE sc_summary
SET capacity = newCapacity
WHERE cno = sc_summary.cno;
END$
DELIMITER ;
执行存储过程。
call proc_updateCourseCapacity('21', 100);
(3)有局部变量的存储过程
定义一个存储过程,更新某个学生的成绩信息。
/*成绩表,记录成绩信息*/
create table grade(
sno char(9) PRIMARY KEY,
avg_grade FLOAT,
avg_weight_grade FLOAT
)
/*存储过程*/
DELIMITER $
CREATE PROCEDURE proc_updateGrade(sname CHAR(20))
BEGIN
/*局部变量声明*/
DECLARE sno CHAR(9);
DECLARE avg_grade SMALLINT;
DECLARE avg_weight_grade FLOAT;
/*查询sno存储到局部变量*/
SELECT student.sno INTO sno
FROM student
WHERE student.sname = sname;
/*计算成绩表-算数平均成绩*/
SELECT SUM(grade) / COUNT(*) INTO avg_grade
FROM SC
WHERE SC.sno = sno;
/*计算加权平均成绩*/
SELECT SUM(grade*Ccredit) / SUM(Ccredit) INTO avg_weight_grade
FROM SC,course
WHERE SC.sno = sno AND SC.cno = course.cno;
/*更新成绩表*/
INSERT INTO grade VALUES (sno, avg_grade, avg_weight_grade)
ON DUPLICATE KEY UPDATE grade.avg_grade = avg_grade, grade.avg_weight_grade = avg_weight_grade;
END$
DELIMITER ;
执行该存储过程。
call proc_updateGrade('李勇');
查看执行结果。
SELECT * FROM grade;
(4)有输出参数的存储过程
定义一个存储过程,更新某个学生的加权平均成绩信息并返回该成绩。
/*存储过程*/
DELIMITER $
CREATE PROCEDURE proc_updateAvgWGrade(sname CHAR(20), out result REAL)
BEGIN
/*局部变量声明*/
DECLARE sno CHAR(9);
DECLARE avg_weight_grade FLOAT;
/*查询sno存储到局部变量*/
SELECT student.sno INTO sno
FROM student
WHERE student.sname = sname;
/*计算加权平均成绩*/
SELECT SUM(grade*Ccredit) / SUM(Ccredit) INTO avg_weight_grade
FROM SC,course
WHERE SC.sno = sno AND SC.cno = course.cno;
SELECT avg_weight_grade INTO result;
/*更新成绩表*/
INSERT INTO grade VALUES (sno, avg_grade, avg_weight_grade)
ON DUPLICATE KEY UPDATE grade.avg_grade = avg_grade, grade.avg_weight_grade = avg_weight_grade;
END$
DELIMITER ;
执行该存储过程,并得到返回结果。
set @result = 0;
call proc_updateAvgWGrade('李勇', @result);
select @result;
(5)修改存储过程
在MYSQL中,ALTER PROCEDURE 语句用于修改存储过程的某些特征。如果要修改存储过程的内容,可以先删除原存储过程,再以相同的命名创建新的存储过程;如果要修改存储过程的名称,可以先删除原存储过程,再以不同的命名创建新的存储过程。
MySQL 中修改存储过程的语法格式如下:
ALTER PROCEDURE 存储过程名 [ 特征 … ]
特征指定了存储过程的特性,可能的取值有:
- CONTAINS SQL 表示子程序包含 SQL 语句,但不包含读或写数据的语句。
- NO SQL 表示子程序中不包含 SQL 语句。
- READS SQL DATA 表示子程序中包含读数据的语句。
- MODIFIES SQL DATA 表示子程序中包含写数据的语句。
- SQL SECURITY { DEFINER |INVOKER } 指明谁有权限来执行。
- DEFINER 表示定义者有对存储过程中对象的访问权限,调用者就能够执行。
- INVOKER 表示调用者必须拥有存储过程中的对象的访问权限,才能执行。
- COMMENT ‘string’ 表示注释信息。
为存储过程添加注释信息:
ALTER PROCEDURE proc_updateAvgWGrade
COMMENT "计算指定学生的平均加权成绩";
指定只有定义者权限。
ALTER PROCEDURE proc_updateAvgWGrade MODIFIES SQL DATA SQL SECURITY DEFINER;
SHOW CREATE PROCEDURE proc_updateAvgWGrade;
(6)删除存储过程
删除存储过程proc_updateAvgWGrade。
DROP PROCEDURE proc_updateAvgWGrade;
5.实验总结
用户自定义函数和存储过程可以可以带有IN, OUT, INOUT参数。在MYSQL中,获取OUT值需要先声明一个变量,然后执行函数或存储过程,获取结果,不能传入NULL获取结果集。存储过程不可以修改名称或内容,只可以修改其特征。用户自定义函数只能通过SELECT调用,而存储过程只能通过call调用。
6.思考题
(1)试总结几种调试存储过程的方法
可以通过在存储过程中添加RAISE语句进行调试,也可以通过OUT参数测试存储过程中的中间结果是否正确。
(2)存储过程中的SELECT语句与普通的SELECT语句格式有何不同?执行方法有何不同?
存储过程中的SELECT语句和普通的SELECT语句的不同在于存储过程中的SELECT语句将值通过INTO子句赋值给变量,而普通的SELECT语句一般直接返回结果集。
实验6.2 自定义函数实验
1.实验目的
掌握数据库PL/SQL编程语言以及数据库自定义函数的设计和使用方法。
2.实验内容和要求
自定义函数定义,自定义函数运行,自定义函数更名,自定义函数删除,自定义函数的参数传递。掌握PL/SQL和编程规范,规范设计自定义函数。
3.实验重点和难点
实验重点:自定义函数的定义和运行。
实验难点:自定义函数的参数传递方法。
4.实验过程
(1)无参数的自定义函数
定义一个自定义函数,更新支出表的教材总费用,并返回该费用。由于MYSQL启用了二进制日志,需要修改log_bin_trust_function_creators为TRUE,表示当信任存储函数创建者,不会创建写入二进制日志引起不安全事件的存储函数。
/*与6.1中的存储过程类似,将结果返回*/
set global log_bin_trust_function_creators=TRUE;
DELIMITER $
CREATE FUNCTION func_calTextBookExpense()
RETURNS REAL
BEGIN
DECLARE res REAL;
UPDATE expenditure
SET amount = (
SELECT SUM(total_price)
FROM textbook_order)
WHERE expenditure_type = "textbook";
SELECT SUM(total_price) INTO res
FROM textbook_order;
RETURN res;
END$
DELIMITER ;
调用该函数,返回值以结果集的方式返回并显示。
SELECT func_calTextBookExpense();
(2)有参数的自定义函数
定义一个自定义函数,更新特定课程的容量,返回剩余容量。
DELIMITER $
CREATE FUNCTION func_updateCourseCapacity(cno CHAR(9), newCapacity SMALLINT)
RETURNS REAL
BEGIN
DECLARE res REAL;
UPDATE sc_summary
SET capacity = newCapacity
WHERE cno = sc_summary.cno;
SELECT capacity - snum INTO res
FROM sc_summary
WHERE cno = sc_summary.cno;
RETURN res;
END$
DELIMITER ;
调用该函数。
SELECT proc_updateCourseCapacity('21', 100);
(3)有局部变量的自定义函数
定义一个函数,更新某个学生的加权平均成绩信息并返回该成绩。
DELIMITER $
CREATE FUNCTION func_updateAvgWGrade(sname CHAR(20))
RETURNS FLOAT
BEGIN
/*局部变量声明*/
DECLARE sno CHAR(9);
DECLARE avg_weight_grade FLOAT;
/*查询sno存储到局部变量*/
SELECT student.sno INTO sno
FROM student
WHERE student.sname = sname;
/*计算加权平均成绩*/
SELECT SUM(grade*Ccredit) / SUM(Ccredit) INTO avg_weight_grade
FROM SC,course
WHERE SC.sno = sno AND SC.cno = course.cno;
/*更新成绩表*/
INSERT INTO grade VALUES (sno, avg_grade, avg_weight_grade)
ON DUPLICATE KEY UPDATE grade.avg_grade = avg_grade, grade.avg_weight_grade = avg_weight_grade;
/*返回结果*/
RETURN avg_weight_grade;
END$
DELIMITER ;
调用该函数。
SELECT func_updateAvgWGrade('李勇');
(4)修改自定义函数
类似于存储过程,修改自定义函数只能修改其特性,不能修改名称或函数体。
/*添加注释*/
ALTER FUNCTION func_updateAvgWGrade COMMENT "使用函数计算指定学生的平均加权成绩";
/*修改函数为定义者权限*/
ALTER FUNCTION func_updateAvgWGrade MODIFIES SQL DATA SQL SECURITY DEFINER;
(5)删除自定义函数
删除func_updateAvgWGrade函数。
DROP FUNCTION func_updateAvgWGrade;
5.实验总结
MYSQL中,自定义函数需要在定义时声明返回值的类型,并在函数结束后显示使用RETURN语句返回值。MYSQL的FUNCTION语句只能通过RETURN返回值,不能通过OUT参数返回多个值,且只能通过SELECT语句调用,不能使用call语句调用。
6.思考题
(1)试分析自定义函数与存储过程的区别与联系。
自定义函数和存储过程都用于执行一些SQL语句,完成计算值等操作。
二者的区别在于:
- 存储过程只能单独使用CALL函数调用,而函数在SELECT语句中,像内置函数一样调用。
- 存储过程可以有IN, OUT参数,返回多个值。而函数只能返回一个值。
- 存储过程可以调用函数,函数不能使用存储过程。
(2)如何使得自定义函数可以返回多个值?如何利用?
MYSQL中,自定义函数不可以使用OUT或INOUT参数,因此只能返回一个值。其他数据库管理系统例如POSTGRELSQL,可以指定OUT参数返回多个值,并且返回值的类型可以是TABLE,即可以返回一个结果集。对于OUT参数,可以先定义一个变量,然后调用函数并传入该变量,得到返回值结果。
实验6.3 游标实验
1.实验目的
掌握PL/SQL游标的设计、定义和使用方法,理解PL/SQL游标按行操作和SQL按结果集操作的区别和联系。
2.实验内容和要求
游标定义、游标使用。掌握各种类型游标的特点、区别与联系。
3.实验重点和难点
实验重点:游标定义和使用。
实验难点:游标类型。
4.实验过程
(1)普通游标
定义一个存储过程,更新sc_summary表中每门课程的选课人数。
DELIMITER $
CREATE PROCEDURE procCursor_calSCnum()
BEGIN
DECLARE sc_cno CHAR(9);
DECLARE sc_num SMALLINT;
DECLARE done BOOL;
DECLARE mycursor CURSOR FOR
SELECT Cno
FROM sc_summary;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN mycursor;
SELECT FALSE INTO done;
WHILE NOT done DO/*处理每一条选课统计记录*/
FETCH mycursor INTO sc_cno;
/*计算选课人数*/
SELECT COUNT(*) INTO sc_num
FROM SC
WHERE sc.Cno = sc_cno;
/*更新选课人数*/
UPDATE sc_summary
SET sc_summary.snum = sc_num
WHERE sc_summary.Cno = sc_cno;
END WHILE;
/*关闭游标*/
CLOSE mycursor;
END$
DELIMITER ;
执行存储过程,并查询选课统计表,选课人数被更新。
CALL procCursor_calSCnum();
select * from sc_summary;
(2)REFCURSOR类型游标
REF CURSOR是动态游标,一般的CURSOR是静态游标。一般的CURSOR在定义时结果集的类型就是确定的,不可以再更改,而动态游标可以打开任何形式的结果集,取出任何类型的记录数据。在MYSQL中只能使用静态游标,定义时的SELECT语句必须确定,不可以再更改。
(3)带参数的游标
MYSQL中游标不可以携带参数。
5.实验总结
游标对数据库记录逐条处理,而不是对结果集一起处理。声明游标和游标结束处理的异常需要按顺序声明。REF CURSOR是动态游标,可以在打开该游标时指定SELECT语句产生游标的结果集,游标的结果可以使用RECORD类型保存。在MYSQL中不支持动态游标,也没有RECORD类型,游标也不可以使用参数,只能声明变量完成处理。
6.思考题
试分析说明REFCURSOR类型游标的优点。
REFCURSOR类型的游标使用灵活,可以多次打开不同的SELECT结果集,实现对不同类型数据的逐条处理。如果使用一般的游标,只能提前将所有需要处理的结果集的SELECT语句都声明一个游标使用。
的类型就是确定的,不可以再更改,而动态游标可以打开任何形式的结果集,取出任何类型的记录数据。在MYSQL中只能使用静态游标,定义时的SELECT语句必须确定,不可以再更改。
(3)带参数的游标
MYSQL中游标不可以携带参数。
5.实验总结
游标对数据库记录逐条处理,而不是对结果集一起处理。声明游标和游标结束处理的异常需要按顺序声明。REF CURSOR是动态游标,可以在打开该游标时指定SELECT语句产生游标的结果集,游标的结果可以使用RECORD类型保存。在MYSQL中不支持动态游标,也没有RECORD类型,游标也不可以使用参数,只能声明变量完成处理。
6.思考题
试分析说明REFCURSOR类型游标的优点。
REFCURSOR类型的游标使用灵活,可以多次打开不同的SELECT结果集,实现对不同类型数据的逐条处理。如果使用一般的游标,只能提前将所有需要处理的结果集的SELECT语句都声明一个游标使用。