使用存储过程

Posted 霖行

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用存储过程相关的知识,希望对你有一定的参考价值。

使用存储过程

本章介绍什么是存储过程,为什么要使用存储过程以及如何使用存储过程,并且介绍创建和使用存储过程的基本语法。

存储过程

目前使用的大多数SQL语句都是针对一个或多个表的单条语句。但实践中经常会有一个完整的操作需要多条语句,例如:

  1. 为了处理订单,需要核对以保证库存中有相应的物品。
  2. 如果库存有物品,这些物品需要预定以便不将它卖给别人,并且库存量需要减少。
  3. 库存中没有的物品需要订购,这需要与供应商进行交互。
  4. 关于哪些物品入库(并且可以立即发货)和哪些物品退订,需要通知相应的客户。

执行这个处理需要针对多条表的多条mysql语句,并且,需要执行的具体语句及次序也不是固定的。因此我们可以创建存储过程,简单来说,它就是为以后的使用而保存的一条或多条MySQL语句的集合。


为什么要使用存储过程

使用存储过程的理由:

  • 通过把处理封装在容易使用的单元中 ,简化复杂的操作。
  • 由于不要求反复建立一系列处理步骤 ,这保证了数据的完整性。
  • 简化对变动的管理。
  • 提高性能 。使用存储过程比使用单独的SQL语句快。
  • 存在一些只能在单个请求中的MySQL元素和特性,存储过程可以使用它们来编写功能更强更灵活的代码。

总的来说,使用存储过程有3个主要的好处,简单、安全、高性能。

但,有好有坏,存储过程有一些缺陷:

  • 一般存储过程的编写比基本SQL语句复杂。
  • 你可能没有创建存储过程的安全访问权限。许多数据库管理员限制了存储过程的创建权限,仅允许用户使用存储过程,但不允许创建存储过程。

使用存储过程

使用存储过程需要知道如何执行它们。存储过程的执行远比其定义更经常遇到,故我们先了解如何执行存储过程,再了解创建和使用存储过程。

执行存储过程

MySQL称存储过程的执行为 调用 ,因此MySQL执行存储过程的语句为CALL。CALL接受存储过程的名字以及需要传递给它的任意参数:

CALL productpricing(@pricelow, @pricehigh, @priceaverage);

执行名为productpricing的存储过程,它计算并返回产品的最低、最高和平均价格。

存储过程可以显示也可以不显示结果。

创建存储过程

创建一个返回产品平均价格的存储过程:

mysql> DELIMITER //
mysql> CREATE PROCEDURE productpricing()
    -> BEGIN
    ->     SELECT Avg(prod_price) AS priceaverage
    ->     FROM products;
    -> END //
Query OK, 0 rows affected (0.04 sec)

mysql> DELIMITER ;

此存储过程名为productpricing,用CREATE PROCEDURE productpricing()语句定义。如果存储过程接受参数,它们将在()中列出。BEGIN和END语句用来限定存储过程体。

MySQL处理这段代码时,它创建一个新的存储过程productpricing,没有返回数据。

MySQL命令行客户机的分隔符 默认的MySQL语句分隔符为 ‘;’ 。MySQL命令行实用程序也使用 ‘;’ 作为语句分隔符。在创建存储过程时,存储过程体中的 ‘;’ 会被命令行解析为语句结束,从而提交不全代码,导致报错。
我们通过 临时更改 命令行实用程序的语句分隔符,来避免冲突。DELIMITER //告诉命令行使用//代替;作为语句分隔符,语句中END后接//表示语句结束。
除\\符合外,任何字符都可以用作语句分隔符。若使用其他客户端,需将DELIMITER行删除,并将END后的//改为;。

使用此存储过程:

mysql> CALL productpricing();
+--------------+
| priceaverage |
+--------------+
|    16.133571 |
+--------------+
1 row in set (0.04 sec)

Query OK, 0 rows affected (0.04 sec)

删除存储过程

为删除存储过程,可以使用DROP PROCEDURE关键字:

mysql> DROP PROCEDURE productpricing;
Query OK, 0 rows affected (0.02 sec)

删除存储过程只需给出存储过程名,不需要带括号与参数。

  • 仅当存在时删除 如果指定的过程不存在,则会产生一个错误。可以使用DROP PROCEDURE IF EXISTS,仅当存储过程存在时执行命令。

使用参数

productpricing它简单地显示SELECT语句的结果。但一般,存储过程并不显示结果,而是把结果返回给你指定的变量。

  • 变量(variable) 内存中一个特定的位置,用来临时存储数据。

productpricing实用版:

mysql> DELIMITER //
mysql> CREATE PROCEDURE productpricing(
    ->     OUT pl DECIMAL(8,2),
    ->     OUT ph DECIMAL(8,2),
    ->     OUT pa DECIMAL(8,2)
    -> )
    -> BEGIN
    ->     SELECT Min(prod_price)
    ->     INTO pl
    ->     FROM products;
    ->     SELECT Max(prod_price)
    ->     INTO ph
    ->     FROM products;
    ->     SELECT Avg(prod_price)
    ->     INTO pa
    ->     FROM products;
    -> END //
Query OK, 0 rows affected (0.01 sec)

mysql> DELIMITER ;

此存储过程接受3个参数:pl存储产品最低价格,ph存储产品最高价格,pa存储产品平均价格。每个参数必须具有指定的类型。关键字OUT指出此参数从存储过程中传出一个值(给调用者)。MySQL支持IN(传递给存储过程)、OUT(从存储过程传出)和INOUT(对存储过程传入和传出)类型的参数。 存储过程体中检索出的值,通过INTO关键字保存到相应变量。

  • 参数的数据类型 存储过程的参数允许的数据类型与表中使用的数据类型相同。因此,记录集不能作为参数进行传递。

为调用此存储过程,必须指定三个变量名:

mysql> CALL productpricing(@pricelow, @pricehigh, @priceaverage);
Query OK, 1 row affected, 1 warning (0.01 sec)
  • 变量名 所有MySQL变量都必须以@开始。

此语句不显示数据,它返回可以显示的变量。

查看变量:

mysql> SELECT @priceaverage;
+---------------+
| @priceaverage |
+---------------+
|         16.13 |
+---------------+
1 row in set (0.00 sec)

创建存储过程ordertotal接受订单号并返回该订单的合计:

mysql> DELIMITER //
mysql> 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 //
Query OK, 0 rows affected (0.01 sec)

mysql> DELIMITER ;

onumber定义为IN,外部将订单号传入存储过程。ototal定义为OUT,从存储过程返回合计。

调用新存储过程:

mysql> CALL ordertotal(20005, @total);
Query OK, 1 row affected (0.01 sec)

显示合计:

mysql> SELECT @total;
+--------+
| @total |
+--------+
| 149.87 |
+--------+
1 row in set (0.00 sec)

建立智能存储过程

以上都是存储过程对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 adding 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;

    -- Add finally, save to out variable
    SELECT total INTO ototal;
END;

此存储过程添加了注释(前面放--),在面对复杂代码时,注释必不可少,方便以后对代码的理解。添加了布尔参数taxable,值为真表示需要增加税。存储过程体中,用DECLARE语句定义了两个局部变量。DECLARE要求指定变量名和数据类型,它也支持可选的默认值。

调用存储过程:

mysql> CALL ordertotal(20005, 0, @total);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT @total;
+--------+
| @total |
+--------+
| 149.87 |
+--------+
1 row in set (0.00 sec)

mysql> CALL ordertotal(20005, 1, @total);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT @total;
+--------+
| @total |
+--------+
| 158.86 |
+--------+
1 row in set (0.00 sec)

BOOLEAN值指定为1表示真,指定为0表示假(实际上,非0即真)。

  • COMMENT关键字 存储过程在CREATE PROCEDURE语句中包含了一个COMMENT值。这不是必须的,但若给出,此值将在SHOW PROCEDURE STATUS的结果中显示。
  • IF语句 此例给出了MySQL的IF语句的基本用法。IF语句还支持ELSEIF和ELSE子句。

检查存储过程

为显示创建存储过程的CREATE语句,使用SHOW CREATE PROCEDURE语句:

mysql> SHOW CREATE PROCEDURE ordertotal;

为了获得包括何时、由谁创建等详细信息的存储过程列表,可使用SHOW PROCEDURE STATUS。

  • 限制过程状态结果 SHOW PROCEDURE STATUS列出所有存储过程。为指定查询,可使用LIKE指定一个过滤模式。
    SHOW PROCEDURE STATUS LIKE 'ordertotal';
    

以上是关于使用存储过程的主要内容,如果未能解决你的问题,请参考以下文章

使用存储过程

使用存储过程

MySQL ------ 存储过程 (二十五)

存储营业时间/节假日

手机电子券怎么用?

如何在flutter firebase中存储和使用营业时间信息?