前言
简单整理一下存储过程。
正文
需要MySQL 5 MySQL 5添加了对存储过程的支持,因此,本章内容适用于MySQL 5及以后的版本。
迄今为止,使用的大多数SQL语句都是针对一个或多个表的单条语句。
并非所有操作都这么简单,经常会有一个完整的操作需要多条语句才能完成。
例如,考虑以下的情形。
-
为了处理订单,需要核对以保证库存中有相应的物品。
-
如果库存有物品,这些物品需要预定以便不将它们再卖给别的人,并且要减少可用的物品数量以反映正确的库存量。
-
库存中没有的物品需要订购,这需要与供应商进行某种交互。
-
关于哪些物品入库(并且可以立即发货)和哪些物品退订,需要通知相应的客户。
既然我们知道了什么是存储过程,那么为什么要使用它们呢?有许多理由,下面列出一些主要的理由。
-
通过把处理封装在容易使用的单元中,简化复杂的操作(正如前面例子所述)。
-
由于不要求反复建立一系列处理步骤,这保证了数据的完整性。如果所有开发人员和应用程序都使用同一(试验和测试)存储过程,则所使用的代码都是相同的。
这一点的延伸就是防止错误。需要执行的步骤越多,出错的可能性就越大。防止错误保证了数据的一致性。
- 简化对变动的管理。如果表名、列名或业务逻辑(或别的内容)有变化,只需要更改存储过程的代码。使用它的人员甚至不需要知道这些变化。
这一点的延伸就是安全性。通过存储过程限制对基础数据的访问减少了数据讹误(无意识的或别的原因所导致的数据讹误)的机会。
-
提高性能。因为使用存储过程比使用单独的SQL语句要快。
-
存在一些只能用在单个请求中的MySQL元素和特性,存储过程可以使用它们来编写功能更强更灵活的代码
换句话说,使用存储过程有3个主要的好处,即简单、安全、高性能。
显然,它们都很重要。不过,在将SQL代码转换为存储过程前,也必须知道它的一些缺陷。
-
一般来说,存储过程的编写比基本SQL语句复杂,编写存储过程需要更高的技能,更丰富的经验。
-
你可能没有创建存储过程的安全访问权限。许多数据库管理员限制存储过程的创建权限,允许用户使用存储过程,但不允许他们创建存储过程。
不能编写存储过程?你依然可以使用 MySQL将编写存储过程的安全和访问与执行存储过程的安全和访问区分开来。这是好事情。
即使你不能(或不想)编写自己的存储过程,也仍然可以在适当的时候执行别的存储过程。
创建存储过程
create PROCEDURE productpricing()
BEGIN
SELECT AVG(prod_price) as priceaverage
from (products);
end;
执行存储过程
call productpricing
mysql命令行客户机的分隔符 如果你使用的是mysql命令行
实用程序,应该仔细阅读此说明。
默认的MySQL语句分隔符为;(正如你已经在迄今为止所使用
的MySQL语句中所看到的那样)。mysql命令行实用程序也使
用;作为语句分隔符。如果命令行实用程序要解释存储过程自
身内的;字符,则它们最终不会成为存储过程的成分,这会使
存储过程中的SQL出现句法错误。
解决办法是临时更改命令行实用程序的语句分隔符,如下所示:
其中,DELIMITER //告诉命令行实用程序使用//作为新的语
句结束分隔符,可以看到标志存储过程结束的END定义为END
//而不是END;。这样,存储过程体内的;仍然保持不动,并且
正确地传递给数据库引擎。最后,为恢复为原来的语句分隔符,
可使用DELIMITER ;。 除符号外,任何字符都可以用作语句分隔符。
如果你使用的是mysql命令行实用程序,在阅读本章时请记住
这里的内容。
删除存储过程
drop PROCEDURE productpricing;
参数
CREATE PROCEDURE productpricing(
out p1 DECIMAL(8,2),
out p2 DECIMAL(8,2),
out p3 DECIMAL(8,2))
BEGIN
SELECT MIN(prod_price) INTO p1
FROM products;
SELECT MAX(prod_price) INTO p2
FROM products;
SELECT AVG(prod_price) INTO p3
FROM products;
END;
这种参数可以为我们输出值。
CALL productpricing(@pricelow,@pricelow,@priceaverage)
这样是没有返回结果的。
看到受影响的结果为一行。
select @pricelow,@pricelow,@priceaverage
这样就有返回结果了。
再举一个in 和out的例子。
create PROCEDURE ordertotal(in onumber INT,
OUT ototal DECIMAL(8,2)
)
BEGIN
SELECT SUM(item_price*quantity)
FROM orderitems
WHERE order_num=onumber
INTO ototal;
END
调用:
CALL ordertotal(20005,@total)
查询:
SELECT @total
迄今为止使用的所有存储过程基本上都是封装MySQL简单的SELECT语句。
虽然它们全都是有效的存储过程例子,但它们所能完成的工作你直接用这些被封装的语句就能完成(如果说它们还能带来更多的东西,那就是使事情更复杂)。
只有在存储过程内包含业务规则和智能处理时,它们的威力才真正显现出来。
考虑这个场景。你需要获得与以前一样的订单合计,但需要对合计增加营业税,不过只针对某些顾客(或许是你所在州中那些顾客)。
那么,你需要做下面几件事情:
-
获得合计(与以前一样);
-
把营业税有条件地添加到合计;
-
返回合计(带或不带税)。
存储过程的完整工作如下:
-- name:ordertotal
-- parameters:onumber = order number
-- taxable = 0 if not taxable,1 if taxable
-- ototal = order total variable
create PROCEDURE ordertotal(
IN onumber INT,
IN taxable Boolean,
OUT ototal DECIMAL(8,2)
) COMMENT "obtain order total, optionally add tax"
BEGIN
-- DECLARE variable for total
DECLARE total DECIMAL(8,2);
-- DECLARE tax percentage
DECLARE taxrate INT DEFAULT 6;
-- Get the order total
SELECT SUM(item_price*quantity)
from orderitems
WHERE order_num = onumber
INTO total;
-- is this taxable?
IF taxable THEN
-- yes, so add taxrate to the total
SELECT total + (total/100*taxrate) INTO total;
END IF;
-- And finally, save to out variable
select total into ototal;
END
此存储过程有很大的变动。首先,增加了注释(前面放置--)。
在存储过程复杂性增加时,这样做特别重要。添加了另外一个参数taxable,它是一个布尔值(如果要增加税则为真,否则为假)。
在存储过程体中,用DECLARE语句定义了两个局部变量。
DECLARE要求指定变量名和数据类型,它也支持可选的默认值(这个例子中的taxrate的默认被设置为6%)。
SELECT语句已经改变,因此其结果存储到total(局部变量)而不是ototal。
IF语句检查taxable是否为真,如果为真,则用另一SELECT语句增加营业税到局部变量total。
最后,用另一SELECT语句将total(它增加或许不增加营业税)保存到ototal。
COMMENT关键字 本例子中的存储过程在CREATE PROCEDURE语
句中包含了一个COMMENT值。它不是必需的,但如果给出,将
在SHOW PROCEDURE STATUS的结果中显示。
执行:
IF语句 这个例子给出了MySQL的IF语句的基本用法。IF语句还支持ELSEIF和ELSE子句(前者还使用THEN子句,后者不使用)。
为显示用来创建一个存储过程的CREATE语句,使用SHOW CREATE PROCEDURE语句:
show CREATE PROCEDURE ordertotal;
结
下一节简单介绍一下游标。