MYSQL存储过程注释详解
Posted 知其黑、受其白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MYSQL存储过程注释详解相关的知识,希望对你有一定的参考价值。
阅读目录
- 1 MySQL存储过程简介及优缺点
- 2 MySQL 存储过程入门
- 3 MySQL 存储过程的变量
- 4 MySQL 存储过程参数
- 5 MySQL 存储过程返回多个值
- 6 MySQL if 语句
- 7 MySQL CASE 语句
- 8 MySQL 存储过程循环
- 9 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
,并把括号放在存储过程的名字之后。
BEGIN
和END
之间的部分称为存储过程的主体。
将声明性 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,OUT
或 INOUT
。
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存储过程详解