往往看别人的代码会有这样的感慨:

看不懂

理还乱

是离愁

别是一番滋味在心头

为什么要使用存储过程?

在mysql开发中使用存储过程的理由:

  1. 当希望在不同的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是非常有用的
  2. mysql 执行语句是要先编译,然后再执行的。这样如果查询并发大的时候。会浪费很多资源和时间。造成mysql进程占用资源过多,症状就是慢。但存储过程可以把一些特别的语句封装成一个方法 ,再编译好成一个可以执行的方法,对外只要接收参数就可以了。这样就不用再编译。执行就快了。你觉得你数据库因为同时出现太多读写操作而变得慢 ,那么就要用了
  3. 使用了存过程,很多相似性的删除,更新,新增等操作就变得轻松了,并且以后也便于管理!
  4. 存储过程因为SQL语句已经预编绎过了,因此运行的速度比较快。
  5. 存储过程可以接受参数、输出参数、返回单个或多个结果集以及返回值。可以向程序返回错误原因。
  6. 存储过程可以接受参数、输出参数、返回单个或多个结果集以及返回值。可以向程序返回错误原因。
  7. 存储过程运行比较稳定,不会有太多的错误。只要一次成功,以后都会按这个程序运行。
  8. 存储过程主要是在服务器上运行,减少对客户机的压力。
  9. 存储过程可以包含程序流、逻辑以及对数据库的查询。同时可以实体封装和隐藏了数据逻辑。
  10. 存储过程可以在单个存储过程中执行一系列SQL语句。
  11. 存储过程可以从自己的存储过程内引用其它存储过程,这可以简化一系列复杂语句。

存储过程案例

让我们使用一些简单的示例来了解什么是存储过程,使用下边演示程序的前提是必须正确安装了mysql。

下边的程序来源于[PHP和MySQL WEB开发(4th)]这本书,我们使用mysql中的books数据库,这个数据库中的表有一下几个:

mysql> show tables;
+-----------------+
| Tables_in_books |
+-----------------+
| book_reviews |
| books |
| customers |
| order_items |
| orders |
+-----------------+
5 rows in set (0.00 sec)

我们用到了orders这个表:

mysql> select * from orders;
+---------+------------+--------+------------+
| orderid | customerid | amount | date |
+---------+------------+--------+------------+
| 1 | 3 | 69.98 | 2007-04-02 |
| 2 | 1 | 49.99 | 2007-04-15 |
| 3 | 2 | 74.98 | 2007-04-19 |
| 4 | 3 | 24.99 | 2007-05-01 |
| 5 | 3 | 69.98 | 2007-04-02 |
| 6 | 1 | 49.99 | 2007-04-15 |
| 7 | 2 | 74.98 | 2007-04-19 |
| 8 | 3 | 24.99 | 2007-05-01 |
| 9 | 2 | 69.98 | 2008-09-02 |
| 10 | 3 | 45.90 | 2009-09-09 |
+---------+------------+--------+------------+
10 rows in set (0.00 sec)

示例1

程序basic_stored_procedure.sql

# Basic stored procedure example

delimiter //

create procedure total_orders (out total float)
begin
select sum(amount) into total from orders;
end
// delimiter ;

打开终端并启动mysql,使用有权限的账户登录mysql,使用某个数据库。上边的程序basic_stored_procedure.sql是封装在一个后缀为sql的文件中的,因此要使用mysql调用这个文件。

使用命令source /path/basic_stored_procedure.sql,path是你保存这个文件的路径。

然后使用命令call total_orders(@t);。可以看出,使用关键字call来调用该过程。call total_orders(@t);这个语句将调用total_orders过程并传入一个用来保存结果的变量。要查看该结果,需要查看该变量。使用下边的语句:

select @t;

结果为:

mysql> call total_orders(@t);
Query OK, 1 row affected (0.01 sec) mysql> select @t;
+------------------+
| @t |
+------------------+
| 555.760009765625 |
+------------------+
1 row in set (0.00 sec)

现在我们逐行分析程序basic_stored_procedure.sql中的代码:

  • elimiter // 将语句末尾的分隔符从当前值(这个分隔符通常是分号,除非以前改变了分隔符)改为双斜杠字符。这样做的目的是可以在存储过程中使用分号分隔符,这样mysql就会将分号当做是存储过程的代码,不会执行这些代码

  • create procedure total_orders (out total float) 创建了实际的存储过程,该存储过程的名称是total_orders。他只有一个total参数,该参数是需要计算的值。out表示该参数将被传出或返回

    • 参数也可以声明为in,表示该值必须传入到存储过程。inout表示该值必须被传入,但可以被存储过程修改
    • float 表示参数的类型。在这个例子中将返回所有订单的总数。orders列的类型为float,因此该返回类型也必须是float。可接受的数据类型映射到可供使用的列类型
    • 如果希望使用多个参数,可以提供一个由逗号间隔的参数列表
  • BEGINEND就好比函数中的{ },用来标识一个语句块

  • select sum(amount) into total from orders; 这就是我们实际中使用的查询语句

  • delimiter ; delimiter重新把分隔符定义为分号;

示例2

示例1中是使用的procedure创建过程的方法,在示例2中,我们将使用function来创建函数。函数接受参数并返回一个唯一值。

程序basic_function.sql:

# Basic syntax to create a function

delimiter //

create function add_tax (price float) returns float
return price*1.1; // delimiter ;
  • 参数不必通过in或out指定,因为所有参数都是in,或是输入参数。
  • 在参数列表之后是returns float,它指定了返回值的类型。该值可以使任何有效的mysql类型
  • return price*1.1; 使用return可以返回一个值
  • 这里并没有使用beginend。可以使用它们,但他们不是必须的。就像php或者c中,如果一个语句块只含有一个语句,可以以调用内置函数的相同方式调用一个存储函数

使用方法示例1中有所不同。

select add_tax(100);

结果如下:

mysql> select add_tax(100);
+--------------+
| add_tax(100) |
+--------------+
| 110 |
+--------------+
1 row in set (0.01 sec)

查询或删除存储过程

在定义了过程和函数之后可使用下边语句来查看过程或函数的代码:

查询:

show create procedure total_orders;

show create function add_tax;

删除:

drop procedure total_orders;

drop function add_tax;

局部变量

使用declare语句,可以在begin...end语句块之间声明局部变量,就像函数中的局部变量一样。例如,可以对add_tax()函数进行修改,使其使用一个局部变量来保存税率,如下:

程序basic_function_with_variables.sql

# Basic syntax to create a function

delimiter //

create function add_tax (price float) returns float
begin
declare tax float default 0.10;
return price*(1+tax);
end
// delimiter ;

游标和控制结构(一个更复杂的例子)

在下边的这个例子中,我们将编写一个存储过程,该存储过程将计算出最大金额的订单,并且返回该订单的orderid(很明显一个简单的查询,就可以计算出该数目,但是这个简单的例子只是说明了如何使用游标和控制结构)

程序control_structures_cursors.sql:

# Procedure to find the orderid with the largest amount
# could be done with max, but just to illustrate stored procedure principles delimiter // create procedure largest_order(out largest_id int)
begin
declare this_id int;
declare this_amount float;
declare l_amount float default 0.0;
declare l_id int; declare done int default 0;
declare c1 cursor for select orderid, amount from orders;
declare continue handler for sqlstate '02000' set done = 1; open c1;
repeat
fetch c1 into this_id, this_amount;
if not done then
if this_amount > l_amount then
set l_amount=this_amount;
set l_id=this_id;
end if;
end if;
until done end repeat;
close c1; set largest_id=l_id; end
// delimiter ;

在该存储过程的开始处,声明了一些在存储过程中使用的局部变量:

  • this_id 保存当前行的orderid
  • this_amount 保存当前行的amount
  • l_id 保存最大金额的orderid
  • l_amount 保存最大金额的amount 默认值为0.0
  • done 用于循环中的标记,当循环结束后会被标记为1,默认为0,也就是false

declare continue handler for sqlstate '02000' set done = 1;是一个声明句柄,它类似于存储过程中的一个异常。

这里边有一个关键字continue,这个关键字是和exit关键字相对应的。continue语句会执行完指定操作后继续循环,而exit语句会退出将从最近的begin...end语句块中退出。 在这里的指定的操作就是set done = 1

既然声明了句柄,就要告诉程序句柄在什么时候调用,在这个例子中,当sqlstate '02000'语句被调用时会执行句柄。那么这个sqlstate '02000'是什么意思呢?该句柄将在无法再找到记录行后调用。也就是说当遍历完所有的结果集后就会调用。

因此,declare continue handler for sqlstate '02000' set done = 1;的意思就是当遍历完结果集之后把done的值设为1。

游标(Cursor)是处理数据的一种方法,为了查看或者处理结果集中的数据,游标提供了在结果集中一次以行或者多行前进或向后浏览数据的能力。我们可以把游标当作一个指针,它可以指定结果中的任何位置,然后允许用户对指定位置的数据进行处理。(关于游标的详细信息,请看这篇博文SQLServer游标(Cursor)简介和使用说明

注意,句柄要定义在游标之后,不然会报ERROR 1338 (42000): Cursor declaration after handler declaration错误。

declare c1 cursor for select orderid, amount from orders;

这个游标名称为c1,这只是他要保存内容的定义,该查询还不会执行。使用open c1;来真正运行这个查询。

要获得每一个数据行,必须运行一个fetch语句。可以在一个repeat循环中完成此操作。:

repeat
...
until done end repeat;

只有在循环的尾部才检测done的值,除了使用repeat还可以使用while和loop循环

while condition do
...
end while loop
...
end loop

这些循环没有内置的循环条件,但是可以通过leave语句退出循环。请注意,存储过程不支持for循环

fetch c1 into this_id, this_amount; 这行代码将获得一个数据行,并把查询到的两个属性保存到this_idthis_amount中。

if not done then
if this_amount > l_amount then
set l_amount=this_amount;
set l_id=this_id;
end if;
end if;

close c1; 将关闭这个游标

set largest_id=l_id; 将最大的值赋值给out参数,不能使用局部变量给外部调用。

调用方法:

call largest_order(@l);
select @l;

结果如下:

mysql> call largest_order(@l);
Query OK, 0 rows affected (0.00 sec) mysql> select @l;
+------+
| @l |
+------+
| 3 |
+------+
1 row in set (0.01 sec)

说明

如有错误之处,请给予指出,多谢。

mysql进阶之存储过程的更多相关文章

  1. mysql进阶(二十八)MySQL GRANT REVOKE用法

    mysql进阶(二十八)MySQL GRANT REVOKE用法   MySQL的权限系统围绕着两个概念: 认证->确定用户是否允许连接数据库服务器: 授权->确定用户是否拥有足够的权限执 ...

  2. 【转】MySQL— 进阶

    [转]MySQL— 进阶 目录 一.视图 二.触发器 三.函数 四.存储过程 五.事务 一.视图 视图是一个虚拟表(非真实存在),其本质是[根据SQL语句获取动态的数据集,并为其命名],用户使用时只需 ...

  3. MySQL进阶19--函数的创建(举例)/设置mysql的创建函数的权限/查看(show)/删除(drop) / 举4个栗子

    /*MySQL进阶19 函数 存储过程和函数:都类似于java中的方法; 存储过程和函数通用好处: 1.提高代码的重用性 2.简化操作 好处: 减少操作次数,减少了编译次数,减少了和服务器的连接次数, ...

  4. MySQL进阶篇(02):索引体系划分,B-Tree结构说明

    本文源码:GitHub·点这里 || GitEE·点这里 一.索引简介 1.基本概念 首先要明确索引是什么:索引是一种数据结构,数据结构是计算机存储.组织数据的方式,是指相互之间存在一种或多种特定关系 ...

  5. MySQL进阶篇(03):合理的使用索引结构和查询

    本文源码:GitHub·点这里 || GitEE·点这里 一.高性能索引 1.查询性能问题 在MySQL使用的过程中,所谓的性能问题,在大部分的场景下都是指查询的性能,导致查询缓慢的根本原因是数据量的 ...

  6. MySql通用分页存储过程

    MySql通用分页存储过程 1MySql通用分页存储过程 2 3过程参数 4p_cloumns varchar(500),p_tables varchar(100),p_where varchar(4 ...

  7. mysql 中创建存储过程

    mysql中创建存储过程和存储函数虽相对其他的sql语言相对复杂,但却功能强大,存储过程和存储函数更像是一种sql语句中特定功能的一种封装,这种封装可以大大简化外围调用语句的复杂程度. 首先以表emp ...

  8. mysql 自己定义存储过程和触发器

    mysql 自己定义存储过程和触发器 --存储过程示范 DROP PROCEDURE IF EXISTS PRO_TEST; CREATE PROCEDURE PRO_TEST(IN NUM_IN I ...

  9. MySql数据库学习--存储过程(1)

    在MySQL 5中,终于引入了存储过程这一新特性,这将大大增强MYSQL的数据库处理能力.在本文中将指导读者快速掌握MySQL 5的存储过程的基本知识,带领用户入门. 存储过程介绍 存储过程是一组为了 ...

随机推荐

  1. 微信企业号 获取AccessToken

    目录 1. AccessToken介绍 2. 示例代码 1. AccessToken介绍 1.1 什么是AccessToken AccessToken即访问凭证,业务服务器每次主动调用企业号接口时需要 ...

  2. 干货来袭-整套完整安全的API接口解决方案

    在各种手机APP泛滥的现在,背后都有同样泛滥的API接口在支撑,其中鱼龙混杂,直接裸奔的WEB API大量存在,安全性令人堪优 在以前WEB API概念没有很普及的时候,都采用自已定义的接口和结构,对 ...

  3. 浅谈我对DDD领域驱动设计的理解

    从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...

  4. 前端学HTTP之报文首部

    前面的话 首部和方法配合工作,共同决定了客户端和服务器能做什么事情.在请求和响应报文中都可以用首部来提供信息,有些首部是某种报文专用的,有些首部则更通用一些.本文将详细介绍HTTP报文中的首部 结构 ...

  5. 如何避免git每次提交都输入密码

    在ubuntu系统中,如何避免git每次提交都输入用户名和密码?操作步聚如下:1: cd 回车: 进入当前用户目录下:2: vim .git-credentials (如果没有安装vim 用其它编辑器 ...

  6. python 入门笔记

    1.pip包安装 pip install *** pip 中http和https代理设置(/etc/profile) 2.强制保存 :w !sudo tee % 3.cffi是python调用C的包 ...

  7. AFN解析器里的坑

    AFN框架是用来用来发送网络请求的,它的好处是可以自动给你解析JSON数据,还可以发送带参数的请求AFN框架还可以监测当前的网络状态,还支持HTTPS请求,分别对用的类为AFNetworkReacha ...

  8. fmt标签把时间戳格式化日期

    jsp页面标签格式化日期 <%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="f" %> ...

  9. 数据库设计中的Soft Delete模式

    最近几天有点忙,所以我们今天来一篇短的,简单地介绍一下数据库设计中的一种模式——Soft Delete. 可以说,该模式毁誉参半,甚至有非常多的人认为该模式是一个Anti-Pattern.因此在本篇文 ...

  10. JAVA通信系列三:Netty入门总结

    一.Netty学习资料 书籍<Netty In Action中文版> 对于Netty的十一个疑问http://news.cnblogs.com/n/205413/ 深入浅出Nettyhtt ...