数据库实验6 存储过程实验
前言:游标的mysql代码不懂写,所以没有运行结果
实验6 存储过程实验
实验6.1 存储过程实验
1.实验目的
掌握数据库 PL/SQL 编程语言,以及数据库存储过程的设计和使用方法。
2.实验内容和要求
存储过程定义,存储过程运行,存储过程更名,存储过程删除,存储过程的参数传递。掌握PL/SQL编程语言和编程规范,规范设计存储过程。
3.实验过程
(1)无参数的存储过程
定义一个存储过程,更新所有订单的(含税折扣价)总价。
CREATE PROCEDURE Proc_CalTotalPrice ()
BEGIN
UPDATE orders
SET totalprice = (
SELECT SUM(extendedprice * (1 - discount) * (1 + tax))
FROM lineitem1
WHERE orders.orderkey = lineitem1.orderkey AND lineitem1.orderkey=1
);
END;
执行存储过程Proc_CalTotalPrice
SELECT lineitem1.extendedprice, lineitem1.orderkey, lineitem1.tax, lineitem1.discount, orders.totalprice
FROM lineitem1, orders
WHERE orders.orderkey = 1 AND orders.orderkey = lineitem1.orderkey;
CALL Proc_CalTotalPrice();
SELECT lineitem1.extendedprice, lineitem1.orderkey, lineitem1.tax, lineitem1.discount, orders.totalprice
FROM lineitem1, orders
WHERE orders.orderkey = 1 AND orders.orderkey = lineitem1.orderkey;
可以看到,执行存储过程Proc_CalTotalPrice后,经过计算: totalprice=extendedprice*(tax+1)* (1 - discount)=20000*(1+0.18)*(1-0.09)=21476
可以看到,Proc_CalTotalPrice执行成功
(2)有参数的存储过程
定义一个存储过程,更新给定订单的(含税折扣价)总价。
CREATE PROCEDURE Proc_CalTotalPrice4Order (
okey integer
)
BEGIN
UPDATE Orders
SET totalprice = (
SELECT sum(extendedprice * (1 - discount) * (1 + tax))
FROM Lineitem1
WHERE orders.orderkey = lineitem1.orderkey
AND Lineitem1.orderkey=okey
);
END;
执行存储过程Proc_CalTotalPrice4Order
SELECT lineitem1.extendedprice, lineitem1.orderkey, lineitem1.tax, lineitem1.discount, orders.totalprice
FROM lineitem1, orders
WHERE orders.orderkey = 1 AND orders.orderkey = lineitem1.orderkey;
CALL Proc_CalTotalPrice4Order(1);
SELECT lineitem1.extendedprice, lineitem1.orderkey, lineitem1.tax, lineitem1.discount, orders.totalprice
FROM lineitem1, orders
WHERE orders.orderkey = 1 AND orders.orderkey = lineitem1.orderkey;
可以看到,传入参数1,代表orderkey=1,经过计算: totalprice=extendedprice*(tax+1)* (1 - discount)=30000*(1+0.18)*(1-0.09)=32214
可以看到,Proc_CalTotalPrice4Order执行成功
(3)有局部变量的存储过程
定义一个存储过程,更新某个顾客的所有订单的(含税折扣价)总价。
CREATE PROCEDURE Proc_CalTotalPrice4Customer (
p_name char(25)
)
BEGIN
DECLARE L_custkey integer;
SELECT custkey
INTO L_custkey
FROM Customer1
WHERE name = TRIM(p_name);
UPDATE Orders1
SET totalprice = (
SELECT sum(extendedprice * (1 - discount) * (1 + tax))
FROM Lineitem1
WHERE Orders1.orderkey = Lineitem1.orderkey
AND Orders1.custkey = L_custkey
);
END;
执行存储过程Proc_CalTotalPrice4Customer
SELECT lineitem1.extendedprice, lineitem1.orderkey, lineitem1.tax, lineitem1.discount, orders1.totalprice,Customer1.name
FROM lineitem1, orders1,Customer1
WHERE orders1.orderkey = 1 AND orders1.orderkey = lineitem1.orderkey;
CALL Proc_CalTotalPrice4Customer('张三');
SELECT lineitem1.extendedprice, lineitem1.orderkey, lineitem1.tax, lineitem1.discount, orders1.totalprice,Customer1.name
FROM lineitem1, orders1,Customer1
WHERE orders1.orderkey = 1 AND orders1.orderkey = lineitem1.orderkey;
可以看到,传入参数’张三’,其在Customer1表中的编号为custkey为1,custkey为1对应orders1表中的orderkey为1的元组,经过计算结果正确,即过程Proc_CalTotalPrice4Customer执行成功
(4)有输出参数的存储过程
定义一个存储过程,更新某个顾客的所有订单的(含税折扣价)总价。
CREATE PROCEDURE Proc_CalTotalPrice4Customer1 (
IN p_custname char(25),
OUT p_totalprice real
)
BEGIN
DECLARE L_custkey integer;
SELECT custkey
INTO L_custkey
FROM Customer1
WHERE name = TRIM(p_custname);
UPDATE Orders1
SET totalprice = (
SELECT sum(extendedprice * (1 - discount) * (1 + tax))
FROM Lineitem1
WHERE Orders1.orderkey = Lineitem1.orderkey
AND Orders1.custkey = L_custkey
);
SELECT sum(totalprice)
INTO p_totalprice
FROM Orders1
WHERE custkey = L_custkey;
END;
执行存储过程Proc_CalTotalPrice4Customer1
SELECT lineitem1.extendedprice, lineitem1.orderkey, lineitem1.tax, lineitem1.discount, orders1.totalprice,Customer1.name
FROM lineitem1, orders1,Customer1
WHERE orders1.orderkey = 1 AND orders1.orderkey = lineitem1.orderkey;
CALL Proc_CalTotalPrice4Customer1('张三',@p_totalprice);
SELECT @p_totalprice;
经计算,结果正确,说明过程Proc_CalTotalPrice4Customer1执行成功
(5)修改存储过程
1)修改存储过程名Proc_CalTotalPrice4Order为CalTotalPrice40rder。
ALTER PROCEDURE Proc_CalTotalPrice4Order RENAME TO CalTotalPrice4Order;
2)编译存储过程
alter procedure CalTotalPrice4Orde(okey integer) compile;
(6)删除存储过程
drop procedure Proc_CalTotalPrice;
4.思考题
(1)试总结几种调试存储过程的方法。
答:
(2)存储过程中的SELECT语句与普通的SELECT语句格式有何不同?执行方法有何不同?
答:存储过程中的SELECT语句可以将查询的结果赋给存储过程中的变量。普通的SELECT 语句的作用是从数据库中返回信息。
实验6.2 自定义函数实验
1.实验目的
掌握数据库PL/SQL编程语言以及数据库自定义函数的设计和使用方法。
2.实验内容和要求
自定义函数定义,自定义函数运行,自定义函数更名,自定义函数删除,自定义函数的参数传递。掌握PL/SQL和编程规范,规范设计自定义函数。
3.实验过程
自定义函数 (user-defined function UDF)是一种对MySQL扩展的途径,其用法和内置函数相同。
自定义函数的两个必要条件:
-
参数
-
返回值(必须有)。函数可以返回任意类型的值。
语法如下:
CREATE FUNCTION function_name(parameter_nametype,[parameter_name type,...])
RETURNS {
STRING|INTEGER|REAL}
runtime_body
注意是returns啊,当初找错找了很久都没发现
(1) 自定义显示时间函数
设计一个可以返回当前时间的函数
DELIMITER $$
CREATE FUNCTION formatDate(fdate datetime) RETURNS VARCHAR(255)BEGIN
RETURN date_format(fdate,'%Y年%m月%d日%h时%i分%s秒');
END $$
DELIMITER;
运行结果如下:
SELECT formatDate (now( ));
(2) 无参数的自定义函数
定义一个自定义函数,更新所有订单的(含税折扣价)总价,并返回所有订单的总价之和。
DROP FUNCTION FUN_CalTotalPrice;
CREATE FUNCTION FUN_CalTotalPrice()
RETURNS REAL
BEGIN
DECLARE res REAL;
UPDATE Orders1
SET totalprice = (
SELECT SUM(extendedprice * (1 - discount) * (1 + tax))
FROM Lineitem1
WHERE Orders1.orderkey = Lineitem1.orderkey
);
SELECT SUM(totalprice)
INTO res
FROM Orders1;
RETURN res;
END;
执行自定义函数FUN_CalTotalPrice( )
SELECT FUN_CalTotalPrice();
通过查看Lineitem1表
通过计算,最后的结果为
5000*(1-0.09)*(1+0.18)+2000*(1-0.27)*(1+0.67)=5369+2438.2=7807.2
,和函数返回结果相同,说明函数运行成功
(3)有参数的自定义函数
定义一个自定义函数,更新并返回给定订单的总价
CREATE FUNCTION FUN_CalTotalPrice4Order (
p_okey INTEGER
)
RETURNS REAL
BEGIN
DECLARE res REAL;
UPDATE Orders
SET totalprice = (
SELECT SUM(extendedprice * (1 - discount) * (1 + tax))
FROM Lineitem
WHERE Orders.orderkey = Lineitem.orderkey
AND Lineitem.orderkey = p_okey
);
SELECT SUM(toyalprice)
INTO res
FROM Orders;
RETURN res;
END;
执行自定义函数FUN_CalTotalPrice4Order()
CALL Fun_CalTotalPrice4Order(5365);
(4)有局部变量的自定义函数
定义一个自定义函数,计算并返回某个顾客的所有订单的总价
CREATE FUNCTION FUN_CalTotalPrice4Customer (
p_custname CHAR(25)
)
RETURNS REAL
BEGIN
DECLARE L_custkey INTEGER;
DECLARE res REAL;
SELECT cuskey
INTO L_custkey
FROM Customer
WHERE NAME = trim(p_custname);
UPDATE Orders
SET totalprice = (
SELECT SUM(extendedprice * (1 - discount) * (1 + tax))
FROM Lineitem
WHERE Orders.orderkey = Lineitem.orderkey
AND Orders.custkey = L_custkey
);
SELECT SUM(toyalprice)
INTO res
FROM Orders
WHERE custkey = L_custkey;
RETURN res;
END;
执行自定义函数FUN_CalTotalPrice4Customer()
SELECT FUN_CalTotalPrice4Customer('张三');
(5)有输出参数的自定义函数
定义一个自定义函数,计算并返回某个顾客的所有订单的总价
CREATE FUNCTION FUN_CalTotalPrice4Customer2 (IN p_custname CHAR ( 25 ), OUT p_totalprice REAL ) RETURNS REAL BEGIN
DECLARE
L_custkey INTEGER;
DECLARE
res REAL;
SELECT
cuskey INTO L_custkey
FROM
Customer
WHERE
NAME = trim( p_custname );
UPDATE Orders
SET totalprice = (
SELECT
SUM( extendedprice * ( 1-discount ) * ( 1+tax ) )
FROM
Lineitem
WHERE
Orders.orderkey = Lineitem.orderkey
AND Orders.custkey = L_custkey
);
SELECT
SUM( toyalprice ) INTO p_totalprice
FROM
Orders
WHERE
custkey = L_custkey;
res:= p_totalprice;
RETURN res;
END;
mysql运行不了,不知道怎么改
执行自定义函数FUN_CalTotalPrice4Customer2()
SELECT FUN_CalTotalPrice4Customer2('张三',null);
(6)修改自定义函数
修改自定义函数名FUN_CalTotalPrice4Order 为CalTotalPrice4Order
ALTER FUNCTIONFUN_CalTotalPrice4Order RENAME TO CalTotalPrice4Order ;
编译自定义函数CalTotalPrice4Order
ALTER FUNCTIONFUN_CalTotalPrice4Order(okey INTEGER) COMPILE;
(7)删除自定义函数
删除自定义函数CalTotalPrice4Order。
DROP FUNCTION CalTotalPrice4Order ;
4.思考题
(1)试分析自定义函数与存储过程的区别与联系。
自定义函数与存储过程相似,都是持久性存储模块。
区别:
- 存储过程实现的功能相对复杂,函数针对性较强。
- 存储过程可以返回多个值,函数只能有一个返回值。
- 存储过程一般独立执行,函数可以作为 sql 语句的组成部分来出现。
(2)如何使得自定义函数可以返回多个值?
在函数的内部先定义一个数组,然后把多个要作为返回值的内容,先存入到数组中,然后让函数名字=数组名,这样返回值就是数组了。注意写这一步的时候不要有括号。这么写的话,调用函数的返回值不需要分割那一步,只需要用一般数组变量存储返回值,然后调用数组内容即可。
实验6.3 游标实验
参考 游标
1.实验目的
掌握PL/SQL游标的设计、定义和使用方法,理解 PL/SQL游标按行操作和SQL按结果集操作的区别和联系。
2.实验内容和要求
游标定义,游标使用。掌握各种类型游标的特点、区别与联系。
3.实验过程
游标
游标的设计是一种数据缓冲区的思想,用来存放SQL语句执行的结果。游标是一种能从包括多条数据记录的结果集中每次提取一条记录的机制。
尽管游标能遍历结果中的所有行,但一次只指向一行。
游标的作用就是用于对查询数据库所返回的记录进行遍历,以便进行相应的操作。
特性
游标具有三个属性:
A、不敏感(Asensitive):数据库可以选择不复制结果集
B、只读(Read only)
C、不滚动(Nonscrollable):游标只能向一个方向前进,并且不可以跳过任何一行数据。
游标是针对行操作的,对从数据库中SELECT查询得到的结果集的每一行可以进行分开的独立的相同或不同的操作,是一种分离的思想。游标是面向集合与面向行的设计思想之间的一种桥梁。
游标的主要缺点是性能不高。
游标的开销与游标中进行的操作相关,如果在游标中进行复杂的操作,开销会非常高。如果采用面向集合的SQL语句,扫描成本为O(N);但如果采用面向集合的SQL语句的扫描成本为O(N*N),则使用游标有可能会带来性能上的提升。
游标的缺点是只能一行一行操作。在数据量大的情况下,速度过慢。数据库大部分是面对集合的,业务会比较复杂,而游标使用会有死锁,影响其他的业务操作,不可取。 当数据量大时,使用游标会造成内存不足现象。
游标的处理过程
- 声明游标 declare:没有检索数据,只是定义要使用的 select 语句
- 打开游标 open:打开游标以供使用,用上一步定义的 select 语句把数据实际检索出来
- 检索游标 fetch:对于填有数据的游标,根据需要取出(检索)各行
- 关闭游标 close:在结束游标使用时,必须关闭游标。
一些游标操作
游标的定义
DECLARE cursor_name CURSOR FOR select_statement
打开游标
OPEN cursor_name;
取游标中的数据
FETCH cursor_name INTO var_name [, var_name]...
关闭游标
CLOSE cursor_name;
释放游标
DEALLOCATE cursor_name;
(1)普通游标
① 定义一个存储过程,用游标实现计算所有订单的总价。
CREATE
OR REPLACE PROCEDURE ProcCursor_CalTotalPrice ( ) AS L_orderkey INTEGER;
L_totalprice REAL;
CURSOR mycursor FOR SELECT
orderkey,
totalprice
FROM
Orders;
BEGIN
OPEN mycursor;
LOOP
FETCH mycursor INTO L_orderkey,
L_totalprice;
IF
mycursor % NOTFOUND THEN
EXIT;
END IF;
SELECT
SUM( extendedprice * ( 1-discount ) * ( 1+tax ) ) INTO L_totalprice
FROM
Lineitem
WHERE
orderkey = L_orderkey;
UPDATE Orders
SET totalprice = L_totalprice
WHERE
orderkey = L_orderkey;
END LOOP;
CLOSE mycuesor;
END;
② 执行存储过程ProcCursor_CalTotalPrice()
CALL ProcCursor_CalTotalPrice()
(2)REFCURSOR类型游标
① 定义一个存储过程,用游标实验计算所有订单的总价。
CREATE
OR REPLACE PROCEDURE ProcRefCursor_CalTotalPrice ( ) AS DECLARE
L_orderkey INTEGER;
L_totalprice REAL;
mycuesor REFCURSOR;
BEGIN
OPEN mycursor FOR SELECT
orderkey,
totalprice
FROM
ORDER;
LOOP
FETCH mycursor INTO L_orderkey,
L_totalprice;
IF
mycursor % NOTFOUND THEN
EXIT;
END IF;
SELECT
SUM( extendedprice * ( 1-discount ) * ( 1+tax ) ) INTO L_totalprice
FROM
Lineitem
WHERE
orderkey = L_orderkey;
UPDATE Orders
SET totalprice = L_totalprice
WHERE
orderkey = L_orderkey;
END LOOP;
CLOSE mycursor;
END;
② 执行存储过程ProcRefCursor_CalTotalPrice()
CALL ProcRefCursor_CalTotalPrice();
(3)记录变量与游标
① 定义一个存储过程,用游标实现计算所有订单的总价。
CREATE
OR REPLACE PROCEDURE ProcRefCursor_CalTotalPrice ( ) AS DECLARE
L_totalprice REAL;
res RECORD;
CURSOR mycuesor FOR SELECT
orderkey,
totalprice
FROM
Orders;
BEGIN
OPEN mycursor
LOOP
FETCH mycursor INTO res;
IF
mycursor % NOTFOUND THEN
EXIT;
END IF;
SELECT
SUM( extendedprice * ( 1-discount ) * ( 1+tax ) ) INTO L_totalprice
FROM
Lineitem
WHERE
orderkey = res.orderkey;
UPDATE Orders
SET totalprice = L_totalprice
WHERE
orderkey = res.orderkey;
END LOOP;
CLOSE mycursor;
END;
② 执行存储过程ProcRefCursor_CalTotalPrice()
CALL ProcRefCursor_CalTotalPrice();
(4)带参数的游标
① 定义一个存储过程,用游标实现计算指定国家的用户订单的总价。
CREATE
OR REPLACE PROCEDURE ProcRefCursor_CalTotalPrice ( p_nationname CHAR ( 20 ) ) AS DECLARE
L_totalprice REAL;
res RECORD;
CURSOR mycuesor ( c_nationname CHAR ( 20 ) ) FOR SELECT
O.orderkey,
O.totalprice
FROM
Orders O,
Customer O,
Nation N
WHERE
O.custkey = C.custkey
AND c.nationkey = N.nationkey
AND TRIM( N.NAME ) = TRIM( c_nationname );
BEGIN
OPEN mycursor ( p_nationname );
LOOP
FETCH mycursor INTO res;
IF
mycursor % NOTFOUND THEN
EXIT;
END IF;
SELECT
SUM( extendedprice * ( 1-discount ) * ( 1+tax ) ) INTO L_totalprice
FROM
Lineitem
WHERE
orderkey = res.orderkey;
UPDATE Orders
SET totalprice = L_totalprice
WHERE
orderkey = res.orderkey;
END LOOP;
CLOSE mycursor;
END;
② 执行存储过程ProcParaCursor_CalTotalPrice()
CALL ProcParaCursor_CalTotalPrice('中国');
4.思考题
试分析说明REFCURSOR类型游标的优点。
答:
-
REFCURSOR类型的游标定义一个游标应用变量,只是再打开该类型游标时才指定具体的SELECT语句以便产生游标的结果集。因此,REFCURSOR实质上是定义了一个动态游标,可以灵活方便地根据程序运行时情况的变动设置游标的SELECT查询结果集。
-
游标可以实现对数据库记录逐条处理,而不是整个结果集一起处理,因此,游标是在PL/SQL语言中实现过程化处理的核心功能。
-
记录对于游标结果记录的处理很方便,通过记录变量可以直接访问记录的每个属性,而无需为记录的每个属性定义相应的变量。
5.实验总结
学习了存储过程定义、存储过程运行,存储过程更名,存储过程删除,存储过程的参数传递。掌握 PL/SQL 编程语言和编程规范,规范设计存储过程。
学习了自定义函数定义、自定义函数运行,自定义函数更名,自定义函数删除,自定义函数的参数传递。掌握 PL/SQL 和编程规范,规范设计自定义函数。
ROM
Lineitem
WHERE
orderkey = res.orderkey;
UPDATE Orders
SET totalprice = L_totalprice
WHERE
orderkey = res.orderkey;
END LOOP;
CLOSE mycursor;
END;
② 执行存储过程ProcParaCursor_CalTotalPrice()
```sql
CALL ProcParaCursor_CalTotalPrice('中国');
4.思考题
试分析说明REFCURSOR类型游标的优点。
答:
-
REFCURSOR类型的游标定义一个游标应用变量,只是再打开该类型游标时才指定具体的SELECT语句以便产生游标的结果集。因此,REFCURSOR实质上是定义了一个动态游标,可以灵活方便地根据程序运行时情况的变动设置游标的SELECT查询结果集。
-
游标可以实现对数据库记录逐条处理,而不是整个结果集一起处理,因此,游标是在PL/SQL语言中实现过程化处理的核心功能。
-
记录对于游标结果记录的处理很方便,通过记录变量可以直接访问记录的每个属性,而无需为记录的每个属性定义相应的变量。
5.实验总结
学习了存储过程定义、存储过程运行,存储过程更名,存储过程删除,存储过程的参数传递。掌握 PL/SQL 编程语言和编程规范,规范设计存储过程。
学习了自定义函数定义、自定义函数运行,自定义函数更名,自定义函数删除,自定义函数的参数传递。掌握 PL/SQL 和编程规范,规范设计自定义函数。
学习了游标定义、游标使用。掌握各种类型游标的特点、区别与联系。