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

看不懂

理还乱

是离愁

别是一番滋味在心头

为什么要使用存储过程?

在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. mysql每秒最多能插入多少条数据 ? 死磕性能压测

    前段时间搞优化,最后瓶颈发现都在数据库单点上. 问DBA,给我的写入答案是在1W(机械硬盘)左右. 联想起前几天infoQ上一篇文章说他们最好的硬件写入速度在2W后也无法提高(SSD硬盘) 但这东西感 ...

  2. ASP.NET Aries 入门开发教程9:业务表单的开发

    前言: 经过前面那么多篇的列表的介绍,终于到了大伙期待的表单开发了. 也是本系列的最后一篇文章了! 1:表单页面的权限设置与继承 对于表单页面,权限的设置有两种: 1:你可以选择添加菜单(设置为不显示 ...

  3. iOS开源项目周报0105

    由OpenDigg 出品的iOS开源项目周报第四期来啦.我们的iOS开源周报集合了OpenDigg一周来新收录的优质的iOS开发方面的开源项目,方便iOS开发人员便捷的找到自己需要的项目工具等. He ...

  4. 深入浅出JavaScript之闭包(Closure)

    闭包(closure)是掌握Javascript从人门到深入一个非常重要的门槛,它是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现.下面写下我的学习笔记~ 闭包-无处不 ...

  5. 深入学习jQuery自定义插件

    原文地址:jQuery自定义插件学习 1.定义插件的方法 对象级别的插件扩展,即为jQuery类的实例增加方法, 调用:$(选择器).函数名(参数);      $(‘#id’).myPlugin(o ...

  6. JAVA的内存模型(变量的同步)

    一个线程中变量的修改可能不会立即对其他线程可见,事实上也许永远不可见. 在代码一中,如果一个线程调用了MyClass.loop(),将来的某个时间点,另一个线程调用了MyClass.setValue( ...

  7. Missing Push Notification Entitlement 问题

    最近打包上传是遇到一个问题: 描述: Missing Push Notification Entitlement - Your app includes an API for Apple's Push ...

  8. 三大框架SSH整合

    三大框架SSH整合 -------------------------------Spring整合Hibernate------------------------------- 一.为什么要整合Hi ...

  9. VS2012+EF6+Mysql配置心路历程

    为了学习ORM,选择了EntityFramework,经历了三天两夜的煎熬,N多次错误,在群里高手的帮助下,终于成功,现在将我的心路历程记录下来,一是让自己有个记录,另外就是让其它人少走些弯路. 我的 ...

  10. 【已解决】Https请求——基础连接已经关闭 发送时发生错误

    本人在做商用项目的推送消息功能时,借助第三方推送服务.这里避免有打广告的嫌疑,就不报名字了.由于是通过调用API接口,所以Post方法是自己写的,但是在开发环境是可以正常推送的,但是一上线就出各种问题 ...