MYSQL存储过程注释详解

Posted 知其黑、受其白

tags:

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

阅读目录

1 mysql存储过程简介及优缺点

1.1 存储过程的定义

存储过程是存储在数据库目录中的一段声明性SQL语句。
触发器,其他存储过程以及Java,Python,php等应用程序可以调用存储过程。


自身的存储过程称为递归存储过程。
大多数数据库管理系统支持递归存储过程。
但是,MySQL不支持它。
在MySQL中实现递归存储过程之前,您应该检查MySQL数据库的版本。

1.2 在 MySQL 中存储过程

MySQL是最受欢迎的开源RDBMS,被社区和企业广泛使用。

然而,在它发布的第一个十年期间,它不支持存储过程,存储函数,触发器和事件。
自从MySQL 5.0版本以来,这些功能被添加到MySQL数据库引擎,使其更加灵活和强大。

1.3 MySQL 存储过程的优点

通常存储过程有助于提高应用程序的性能。

当创建,存储过程被编译之后,就存储在数据库中。

但是,MySQL实现的存储过程略有不同。

MySQL存储过程按需编译。

在编译存储过程之后,MySQL将其放入缓存中。

MySQL为每个连接维护自己的存储过程高速缓存。

如果应用程序在单个连接中多次使用存储过程,则使用编译版本,否则存储过程的工作方式类似于查询。

存储过程有助于减少应用程序和数据库服务器之间的流量,因为应用程序不必发送多个冗长的SQL语句,而只能发送存储过程的名称和参数。

存储的程序对任何应用程序都是可重用的和透明的。

存储过程将数据库接口暴露给所有应用程序,以便开发人员不必开发存储过程中已支持的功能。

存储的程序是安全的。 数据库管理员可以向访问数据库中存储过程的应用程序授予适当的权限,而不向基础数据库表提供任何权限。

除了这些优点之外,存储过程有其自身的缺点,在数据库中使用它们之前,您应该注意这些缺点。

1.4 MySQL 存储过程的缺点

如果使用大量存储过程,那么使用这些存储过程的每个连接的内存使用量将会大大增加。

此外,如果您在存储过程中过度使用大量逻辑操作,则CPU使用率也会增加,因为数据库服务器的设计不等于逻辑运算。

存储过程的构造使得开发具有复杂业务逻辑的存储过程变得更加困难。

很难调试存储过程。只有少数数据库管理系统允许您调试存储过程。不幸的是,MySQL不提供调试存储过程的功能。

开发和维护存储过程并不容易。开发和维护存储过程通常需要一个不是所有应用程序开发人员拥有的专业技能。这可能会导致应用程序开发和维护阶段的问题。

MySQL存储过程有自己的优点和缺点。开发应用程序时,您应该决定是否应该或不应该根据业务需求使用存储过程。

2 MySQL 存储过程入门

我们将逐步介绍如何使用 CREATE PROCEDURE 语句开发第一个 MySQL 存储过程。
另外,我们将向您展示如何从SQL语句调用存储过程。

1.1 编写第一个MySQL存储过程

我们将开发一个名为 GetAllProducts() 的简单存储过程来帮助您熟悉创建存储过程的语法。

GetAllProducts() 存储过程从 products 表中选择所有产品。

启动 mysql 客户端工具并键入以下命令:

DELIMITER //
 CREATE PROCEDURE GetAllProducts()
   BEGIN
   SELECT *  FROM products;
   END //
DELIMITER ;

让我们来详细地说明上述存储过程:

第一个命令是 DELIMITER //,它与存储过程语法无关。

DELIMITER 语句将标准分隔符 - 分号 (;) 更改为://
在这种情况下,分隔符从分号 (;) 更改为双斜杠 //

为什么我们必须更改分隔符?
因为我们想将存储过程作为整体传递给服务器,而不是让 mysql 工具一次解释每个语句。 在 END 关键字之后,使用分隔符 // 来指示存储过程的结束。

最后一个命令 (DELIMITER;) 将分隔符更改回分号 (;)

使用 CREATE PROCEDURE 语句创建一个新的存储过程。

CREATE PROCEDURE 语句之后指定存储过程的名称。
在这个示例中,存储过程的名称为:GetAllProducts,并把括号放在存储过程的名字之后。

BEGINEND 之间的部分称为存储过程的主体。

将声明性 SQL 语句放在主体中以处理业务逻辑。
在这个存储过程中,我们使用一个简单的 SELECT 语句来查询 products 表中的数据。

1.2 调用存储过程

要调用存储过程,可以使用以下SQL命令:

CALL STORED_PROCEDURE_NAME();

使用 CALL 语句调用存储过程,例如调用 GetAllProducts() 存储过程,则使用以下语句:

CALL GetAllProducts();


如果您执行上述语句,将查询获得 products 表中的所有产品。

3 MySQL 存储过程的变量

变量是一个命名数据对象,变量的值可以在存储过程执行期间更改。
我们通常使用存储过程中的变量来保存直接/间接结果。
这些变量是存储过程的本地变量。

注意:变量必须先声明后,才能使用它。

3.1 声明变量

要在存储过程中声明一个变量,可以使用 DECLARE 语句,如下所示:

DECLARE variable_name datatype(size) DEFAULT default_value;

下面来更详细地解释上面的语句:

首先,在 DECLARE 关键字后面要指定变量名。
变量名必须遵循 MySQL 表列名称的命名规则。

其次,指定变量的数据类型及其大小。
变量可以有任何 MySQL 数据类型,如 INT,VARCHAR,DATETIME 等。

当声明一个变量时,它的初始值为 NULL。
但是可以使用 DEFAULT 关键字为变量分配默认值。

例如,可以声明一个名为 total_sale 的变量,数据类型为 INT,默认值为 0,如下所示:

DECLARE total_sale INT DEFAULT 0;

MySQL 允许您使用单个 DECLARE 语句声明共享相同数据类型的两个或多个变量,如下所示:

DECLARE x, y INT DEFAULT 0;

我们声明了两个整数变量 x 和 y,并将其默认值设置为 0。

3.2 分配变量值

当声明了一个变量后,就可以开始使用它了。
要为变量分配一个值,可以使用 SET 语句,例如:

DECLARE total_count INT DEFAULT 0;
SET total_count = 10;

上面语句中,分配 total_count 变量的值为 10
除了 SET 语句之外,还可以使用 SELECT INTO 语句将查询的结果分配给一个变量。
请参阅以下示例:

DECLARE total_products INT DEFAULT 0

SELECT COUNT(*) INTO total_products
FROM products

在上面的例子中:
首先,声明一个名为 total_products 的变量,并将其值初始化为 0

然后,使用 SELECT INTO 语句来分配值给 total_products 变量,从 示例数据库(yiibaidb) 中的 products 表中选择的产品数量。

3.3 变量范围(作用域)

一个变量有自己的范围(作用域),它用来定义它的生命周期。

如果在存储过程中声明一个变量,那么当达到存储过程的 END 语句时,它将超出范围,因此在其它代码块中无法访问。

如果您在 BEGIN END 块内声明一个变量,那么如果达到 END,它将超出范围。

可以在不同的作用域中声明具有相同名称的两个或多个变量,因为变量仅在自己的作用域中有效。

但是,在不同范围内声明具有相同名称的变量不是很好的编程习惯。

@ 符号开头的变量是会话变量。

直到会话结束前它可用和可访问。

4 MySQL 存储过程参数

在现实应用中,开发的存储过程几乎都需要参数。
这些参数使存储过程更加灵活和有用。
在 MySQL中,参数有三种模式:IN,OUTINOUT

IN - 是默认模式。
在存储过程中定义IN参数时,调用程序必须将参数传递给存储过程。

另外,IN参数的值被保护。
这意味着即使在存储过程中更改了IN参数的值,在存储过程结束后仍保留其原始值。
换句话说,存储过程只使用IN参数的副本。

OUT - 可以在存储过程中更改 OUT 参数的值,并将其更改后新值传递回调用程序。
请注意,存储过程在启动时无法访问 OUT 参数的初始值。

INOUT - INOUT 参数是 IN 和 OUT 参数的组合。
这意味着调用程序可以传递参数,并且存储过程可以修改 INOUT 参数并将新值传递回调用程序。

在存储过程中定义参数的语法如下:

MODE param_name param_type(param_size)

上面语法说明如下:
根据存储过程中参数的目的,MODE 可以是 IN,OUT 或 INOUT。

param_name 是参数的名称。
参数的名称必须遵循 MySQL 中列名的命名规则。

在参数名之后是它的数据类型和大小。
和变量一样,参数的数据类型可以是任何有效的MySQL数据类型。

如果存储过程有多个参数,则每个参数由逗号 (,) 分隔。

4.1 MySQL存储过程参数示例

1 IN 参数示例

以下示例说明如何使用 GetOfficeByCountry 存储过程中的 IN 参数来查询选择位于特定国家/地区的办公室。

USE `yiibaidb`;
DROP procedure IF EXISTS `GetOfficeByCountry`;

DELIMITER $$
USE `yiibaidb`$$

CREATE PROCEDURE GetOfficeByCountry (IN countryName VARCHAR(255))
BEGIN
	SELECT
		*
	FROM
		offices
	WHERE
		country = countryName ; END$$

DELIMITER ;

countryName 是存储过程的 IN 参数。
在存储过程中,我们查询位于 countryName 参数指定的国家/地区的所有办公室。

假设我们想要查询在美国(USA)的所有办事处,我们只需要将一个值(USA)传递给存储过程,如下所示:

CALL GetOfficeByCountry('USA')

要在法国获得所有办事处,我们将 France 字符串传递给 GetOfficeByCountry 存储过程,如下所示:

CALL GetOfficeByCountry('France')

2 OUT 参数示例

以下存储过程通过订单状态返回订单数量。

它有两个参数:
orderStatus:IN 参数,它是要对订单计数的订单状态。
total:存储指定订单状态的订单数量的 OUT 参数。

以下是 CountOrderByStatus 存储过程的源代码。

USE `yiibaidb`;

DROP PROCEDURE
IF EXISTS `CountOrderByStatus`;

DELIMITER $$
CREATE PROCEDURE CountOrderByStatus (
	IN orderStatus VARCHAR (25),
	OUT total INT
)
BEGIN
	SELECT
		count(orderNumber) INTO total
	FROM
		orders
	WHERE
		STATUS = orderStatus ; END$$
DELIMITER ;


要获取发货订单的数量,我们调用 CountOrderByStatus 存储过程,并将订单状态传递为已发货,并传递参数 (@total) 以获取返回值。

CALL CountOrderByStatus('Shipped',@total);
SELECT @total;

3 INOUT 参数示例

以下示例演示如何在存储过程中使用 INOUT 参数。如下查询语句:

DELIMITER $$
CREATE PROCEDURE set_counter (
	INOUT count INT (4),
	IN inc INT (4)
)
BEGIN

SET count = count + inc ; END$$
DELIMITER ;

上面查询语句是如何运行的 ?

set_counter 存储过程接受一个 INOUT 参数 (count) 和一个 IN 参数 (inc)。

在存储过程中,通过 inc 参数的值增加计数器 (count)。

下面来看看如何调用 set_counter 存储过程:

SET @counter = 1;
CALL set_counter(@counter,1); -- 2
CALL set_counter(@counter,1); -- 3
CALL set_counter(@counter,5); -- 8
SELECT @counter; -- 8

5 MySQL 存储过程返回多个值

MySQL存储函数只返回一个值。
要开发返回多个值的存储过程,需要使用带有 INOUT 或 OUT 参数的存储过程。

如果您不熟悉 INPUT 或 OUT 参数的用法,请查看存储过程参数教程的详细信息。

5.1 返回多个值的存储过程示例

我们来看看示例数据库 (yiibaidb) 中的 orders 表。

mysql> desc orders;
+----------------+-------------+------+-----+---------+-------+
| Field          | Type        | Null | Key | Default | Extra |
+----------------+-------------+------+-----+---------+-------+
| orderNumber    | int(11)     | NO   | PRI | NULL    |       |
| orderDate      | date        | NO   |     | NULL    |       |
| requiredDate   | date        | NO   |     | NULL    |       |
| shippedDate    | date        | YES  |     | NULL    |       |
| status         | varchar(15) | NO   |     | NULL    |       |
| comments       | text        | YES  |     | NULL    |       |
| customerNumber | int(11)     | NO   | MUL | NULL    |       |
+----------------+-------------+------+-----+---------+-------+
7 rows in set

以下存储过程接受客户编号,并返回发货 (shipped),取消 (canceled),解决 (resolved) 和争议 (disputed) 的订单总数。

DELIMITER $$
CREATE PROCEDURE get_order_by_cust (
	IN cust_no INT,
	OUT shipped INT,
	OUT canceled INT,
	OUT resolved INT,
	OUT disputed INT
)
BEGIN
	-- shipped
	SELECT
		count(*) INTO shipped
	FROM
		orders
	WHERE
		customerNumber = cust_no
	AND STATUS = 'Shipped' ; -- canceled
	SELECT
		count(*) INTO canceled
	FROM
		orders
	WHERE
		customerNumber = cust_no
	AND STATUS = 'Canceled' ; -- resolved
	SELECT
		count(*) INTO resolved
	FROM
		orders
	WHERE
		customerNumber = cust_no
	AND STATUS = 'Resolved' ; -- disputed
	SELECT
		count(*) INTO disputed
	FROM
		orders
	WHERE
		customerNumber = cust_no
	AND STATUS = 'Disputed' ;
	END


除 IN 参数之外,存储过程还需要 4 个额外的 OUT 参数:
shipped, canceled, resolved 和 disputed

在存储过程中,使用带有 COUNT 函数的 SELECT 语句根据订单状态获取相应的订单总数,并将其分配给相应的参数。

要使用 get_order_by_cust 存储过程,可以传递客户编号和四个用户定义的变量来获取输出值。执行存储过程后,使用 SELECT 语句输出变量值。

SELECT @shipped,@canceled,@resolved,@disputed;

+----------+-----------+-----------+-----------+
| @shipped | @canceled | @resolved | @disputed |
+----------+-----------+-----------+-----------+
|       22 |         0 |         1 |         1 |
+----------+-----------+-----------+-----------+
1 row in set

5.2 从PHP调用返回多个值的存储过程

以下代码片段显示如何从PHP程序中调用返回多个值的存储过程。

<?php
/**
 * Call stored procedure that return multiple values
 * @param $customerNumber
 */
function call_sp($customerNumber)

    try 
        $pdo = new PDO("mysql:host=localhost;dbname=yiibaidb", 'root', 'root');

        // execute the stored procedure
        $sql = 'CALL get_order_by_cust(:no,@shipped,@canceled,@resolved,@disputed)';
        $stmt = $pdo->prepare($sql);

        $stmt->bindParam(':no', $customerNumber, PDO::PARAM_INT);
        $stmt->execute();
        $stmt->closeCursor();

        // execute the second query to get values from OUT parameter
        $r = $pdo->query("SELECT @shipped,@canceled,@resolved,@disputed")
                  ->fetch(PDO::FETCH_ASSOC);
		/*
		print_r($r);exit;
		Array
		(
			[@shipped] => 22
			[@canceled] => 0
			[@resolved] => 1
			[@disputed] => 1
		)
		*/
        if ($r) 
            printf('Shipped: %d, Canceled: %d, Resolved: %d, Disputed: %d',
                $r['@shipped'],
                $r['@canceled'],
                $r['@resolved'],
                $r['@disputed']);
        
     catch (PDOException $pe) 
        die("Error occurred:" . $pe->getMessage());
    


call_sp(141);
PS D:\\tmp> php .\\dump.php
Shipped: 22, Canceled: 0, Resolved: 1, Disputed: 1
PS D:\\tmp>

@ 符号之前的用户定义的变量与数据库连接相关联,因此它们可用于在调用之间进行访问。

6 MySQL if 语句

MySQL IF 语句允许您根据表达式的某个条件或值结果来执行一组SQL语句。

要在MySQL中形成一个表达式,可以结合文字,变量,运算符,甚至函数来组合。

表达式可以返回 TRUE,FALSE 或 NULL,这三个值之一。

请注意,有一个 IF 函数与本文中指定的 IF 语句是不同的。

6.1 MySQL IF语句语法

下面说明了 IF 语句的语法:

IF expression THEN 
   statements;
END IF;

如果表达式 (expression) 计算结果为 TRUE,那么将执行 statements 语句,否则控制流将传递到 END IF 之后的下一个语句。

以下流程图演示了IF语句的执行过程:

6.2 MySQL IF ELSE 语句

如果表达式计算结果为 FALSE 时执行语句,请使用 IF ELSE 语句,如下所示:

IF expression THEN
   statements;
ELSE
   else-statements;
END IF;

以下流程图说明了 IF ELSE 语句的执行过程:

6.3 MySQL IF ELSEIF ELSE 语句

如果要基于多个表达式有条件地执行语句,则使用 IF ELSEIF ELSE 语句如下:

IF expression THEN
   statements;
ELSEIF elseif-expression THEN
   elseif-statements;
...
ELSE
   else-statements;
END IF;

如果表达式 (expression) 求值为 TRUE,则 IF 分支中的语句 (statements) 将执行;

如果表达式求值为 FALSE,则如果 elseif_expression 的计算结果为 TRUE,MySQL将执行elseif-expression,否则执行 ELSE 分支中的 else-statements 语句。

具体流程如下

6.2 MySQL IF语句示例

以下示例说明如何使用IF ESLEIF ELSE语句,GetCustomerLevel() 存储过程接受客户编号和客户级别的两个参数。

首先,它从 customers 表中获得信用额度
然后,根据信用额度,它决定客户级别:PLATINUM , GOLD 和 SILVER 。
参数 p_customerlevel 存储客户的级别,并由调用程序使用。

USE yiibaidb;

DELIMITER $$

CREATE PROCEDURE GetCustomerLevel(
    in  p_customerNumber int(11), 
    out p_customerLevel  varchar(10)
)
BEGIN
    DECLARE creditlim double;

    SELECT creditlimit INTO creditlim
    FROM customers
    WHERE customerNumber = p_customerNumber;

    IF creditlim > 50000 THEN
			SET p_customerLevel = 'PLATINUM';
    ELSEIF (creditlim <= 50000 AND creditlim >= 10000) THEN
      SET p_customerLevel = 'GOLD';
    ELSEIF creditlim < 10000 THEN
      SET p_customerLevel = 'SILVER';
    END IF;

END $$

call GetCustomerLevel(114,@p_customerLevel);

select @p_customerLevel;

以下流程图演示了确定客户级别的逻辑 :

1 SHOW STATUS 语句查看存储过程

 SHOW PROCEDURE STATUS  [ like ‘pattern’] ;

参数 PROCEDURE 表示查询存储过程;
参数 LIKE ‘pattern’ 用来匹配存储过程的名称。

 show procedure status like 'proc%';

2 使用 SHOW CREATE 语句查看存储过程的定义

SHOW CREATE PROCEDURE proc_name ;|\\G     

参数 PROCEDURE 表示查询存储过程;
参数 proc_name 表示存储过程的名称。

show create procedure proc_age;

3 存储过程的删除

DROP PROCEDURE proc_name;    

关键字 DROP PROCEDURE 用来表示实现删除存储过程;
参数 proc_name 表示所要删除的存储过程名称。

7 MySQL CASE 语句

除了IF语句,MySQL提供了一个替代的条件语句CASE。
MySQL CASE语句使代码更加可读和高效。

7.1 简单 CASE 语句

我们来看一下简单 CASE 语句的语法:

CASE  case_expression
   WHEN when_expression_1 THEN commands
   WHEN when_expression_2 THEN commands
   ...
   ELSE commands
END CASE;

您可以使用简单 CASE 语句来检查表达式的值与一组唯一值的匹配。

case_expression 可以是任何有效的表达式。

我们将 case_expression 的值与每个 WHEN 子句中的 when_expression 进行比较,例如when_expression_1,when_expression_2 等。

如果 case_expression 和 when_expression_n 的值相等,则执行相应的 WHEN 分支中的命令 (commands)。

如果 WHEN 子句中的 when_expression 与 case_expression 的值匹配,则 ELSE 子句中的命令将被执行。

ELSE 子句是可选的。

如果省略 ELSE 子句,并且找不到匹配项,MySQL将引发错误。

以下示例说明如何使用简单的 CASE 语句:

DELIMITER $$

CREATE PROCEDURE GetCustomerShipping(
 in  p_customerNumber int(11), 
 out p_shiping        varchar(50)
)

BEGIN
 DECLARE customerCountry varchar(50);
 SELECT country INTO customerCountry
 FROM customers
 WHERE customerNumber = p_customerNumber;

 CASE customerCountry
 WHEN  'USA' THEN
    SET p_shiping = '2-day Shipping';
 WHEN 'Canada' THEN
    SET p_shiping = '3-day Shipping';
 ELSE
    SET p_shiping = '5-day Shipping';
 END CASE;
END $$

上面存储过程是如何工作的?

GetCustomerShipping 存储过程接受客户编号作为 IN 参数,并根据客户所在国家返回运送时间。

在存储过程中,首先,我们根据输入的客户编号得到客户的国家。然后使用简单CASE语句来比较客户的国家来确定运送期。如果客户位于美国(USA),则运送期为2天。 如果客户在加拿大,运送期为3天。 来自其他国家的客户则需要5天的运输时间。

以下流程图显示了确定运输时间的逻辑。


以下是上述存储过程的测试脚本:

SET @customerNo = 112;

SELECT country into @country
FROM customers
WHERE customernumber = @customerNo;

CALL GetCustomerShipping(@customerNo,@shipping);

SELECT @customerNo AS Customer,
       @country    AS Country,
       @shipping   AS Shipping;

7.2 可搜索 CASE 语句

简单CASE语句仅允许您将表达式的值与一组不同的值进行匹配。

为了执行更复杂的匹配,如范围,您可以使用可搜索CASE语句。

可搜索CASE语句等同于IF语句,但是它的构造更加可读。

以下说明可搜索CASE语句的语法:

CASE
    WHEN condition_1 THEN commands
    WHEN condition_2 THEN commands
    ...
    ELSE commands
END CASE;

MySQL评估求值WHEN子句中的每个条件,直到找到一个值为TRUE的条件,然后执行THEN子句中的相应命令(commands)。

如果没有一个条件为TRUE,则执行ELSE子句中的命令(commands)。如果不指定ELSE子句,并且没有一个条件为TRUE,MySQL将发出错误消息。

MySQL不允许在THEN或ELSE子句中使用空的命令。 如果您不想处理ELSE子句中的逻辑,同时又要防止MySQL引发错误,则可以在ELSE子句中放置一个空的BEGIN END块。

以下示例演示如何使用可搜索CASE语句来根据客户的信用额度来查找客户级:SILVER,GOLD或PLATINUM。

DELIMITER $$

CREATE PROCEDURE GetCustomerLevel(
 in  p_customerNumber int(11), 
 out p_customerLevel  varchar(10)
)
BEGIN
 DECLARE creditlim double;

 SELECT creditlimit INTO creditlim
 FROM customers
 WHERE customerNumber = p_customerNumber;

    CASE  
 WHEN creditlim > 50000 THEN 
    SET p_customerLevel = 'PLATINUM';
 WHEN (creditlim <= 50000 AND creditlim >= 10000) THEN
    SET p_customerLevel = 'GOLD';
 WHEN creditlim < 10000 THEN
    SET p_customerLevel = 'SILVER';
 END CASE;

END $$

在上面查询语句逻辑中,如果信用额度是:

大于50K,则客户是PLATINUM客户。
小于50K,大于10K,则客户是GOLD客户。
小于10K,那么客户就是SILVER客户。

我们可以通过执行以下测试脚本来测试存储过程:

CALL GetCustomerLevel(112,@level);
MySQL存储过程详解

MySql 存储过程实例(附完整注释)

MySql 存储过程实例(附完整注释)

MySQL存储过程详解

mySQL的存储过程详解

MySQL数据库编程02