数据库实验六:存储过程实验

实验六 存储过程实验

实验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;

数据库实验六:存储过程实验_第1张图片

(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语句都声明一个游标使用。

你可能感兴趣的:(数据库,数据库)