写在开头:本文所有的示例都是基于workers表,表中保存了某公司的员工姓名、性别、工资、年龄和居住城市,如下:

+----+-----------+--------+--------+------+----------------+
| id | name | sex | salary | age | city |
+----+-----------+--------+--------+------+----------------+
| 2 | Paul | male | 5170 | 23 | Boston |
| 4 | Robin | male | 8350 | 40 | Beijing |
| 5 | Nina | female | 6700 | 27 | Chicago |
| 6 | Jacky | male | 9000 | 35 | Beijing |
| 7 | Tim | male | 5600 | 29 | Chicago |
| 8 | Katherine | female | 7000 | 32 | Washington D.C |
+----+-----------+--------+--------+------+----------------+

一、定义

存储程序可以分为存储过程和函数。

1.1 存储过程的定义

存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集。存储过程在数据库中经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。

1.2 函数的定义

存储函数(简称函数)在本质上与存储过程没有区别。

只是函数有如:只能返回一个变量的限制,而存储过程可以返回多个。函数是可以嵌入在SQL中使用,可以在select中调用,而存储过程不行。

二、创建存储过程和函数

存储过程和函数的创建过程很相似。

2.1 创建存储过程

创建存储过程使用CREATE PROCEDURE语句:

CREATE PROCEDURE p_name(参数列表)
[characteristics...]
BEGIN
routine_body;
END;

begin和end语句用来限定存储过程体。参数列表可以为空,若不为空,则每个参数的形式如下:

[IN|OUT|INOUT] 参数名 参数类型

其中,IN表示输入参数,OUT表示输出参数,INOUT表示既可以输入也可以输出。

characteristics 是可选的参数,用于指定存储过程的特性:

  • language sql:说明存储过程由SQL语句组成,目前SQL是language特性的唯一值。
  • [not]deterministic:表示结果是确定的,相同的输入会得到相同的输出;加not表示结果不确定,相同的输入可能得到不同的输出。默认为 not deterministic。
  • contains sql|no sql|reads sql data|modifies sql data:
    1. contains sql:存储过程包含SQL语句,但不包含读写数据的语句。
    2. no sql:存储过程不包含SQL语句。
    3. reads sql data:存储过程包含读数据的语句。
    4. modifies sql data:存储过程包含读写数据的语句。
  • sql security {definer|invoker}:指明谁有权限执行存储过程。definer表示只有定义者才能执行,invoker表示拥有权限的调用者可以执行。默认为definer。
  • comment'string':注释信息,可以用来描述存储过程或函数。

2.2 创建函数

创建函数使用CREATE FUNCTION语句:

CREATE FUNCTION f_name(参数列表)
RETURNS type
[characteristics...]
routine_body

参数列表可以为空,若不为空,声明形式与存储过程的声明形式一样。characteristics用于指定函数的特性,取值同上,这里不再赘述。

RETURNS type表示函数的返回类型;routine_body是函数体,函数体中必须包含一个 RETURN value 语句。

三、调用存储过程和函数

下面通过两个实例来说明存储过程、函数的创建和调用过程。

3.1 存储过程示例

创建一个存储过程,用来计算workers表中每个员工的平均工资:

CREATE PROCEDURE getAverageSalary()
BEGIN
SELECT AVG(salary) FROM workers;
END;

实际的执行过程如下:

mysql> DELIMITER //
mysql> CREATE PROCEDURE getAverageSalary()
-> BEGIN
-> SELECT AVG(salary) FROM workers;
-> END//
Query OK, 0 rows affected (0.05 sec) mysql> DELIMITER ;

DELIMITER //语句的作用是将MySQL的结束符设置为//,因为MySQL默认的语句结束符为;,为了避免与存储过程中SQL语句结束符相冲突,需要使用 delimiter 改变存储过程的结束符,并以END//结束存储过程。存储过程定义完毕之后再用DELIMITER ;恢复默认结束符。当然也可以指定其他符号做为结束符。

下面我们调用一下 getAverageSalary() 存储过程,使用CALL命令:

mysql> CALL getAverageSalary();
+-------------+
| AVG(salary) |
+-------------+
| 6970.0000 |
+-------------+
1 row in set (0.20 sec)

3.2 函数示例

同样的,我们创建一个函数来计算平均工资:

CREATE FUNCTION getAverageSalary()
RETURNS DECIMAL(8,4)
RETURN(SELECT AVG(salary) FROM workers);

实际的执行过程如下:

mysql> DELIMITER //
mysql> CREATE FUNCTION getAverageSalary()
-> RETURNS DECIMAL(8,4)
-> RETURN(
-> SELECT AVG(salary) FROM workers
-> )//
Query OK, 0 rows affected (0.11 sec) mysql> DELIMITER ;

在MySQL中,自定义的函数与MySQL内部函数的使用方法是一样的,可以嵌入SQL中。我们用SELECT调用:

mysql> SELECT getAverageSalary();
+--------------------+
| getAverageSalary() |
+--------------------+
| 6970.0000 |
+--------------------+

四、复杂的存储过程和函数

4.1 变量的使用

变量可以在子程序中声明并使用,这些变量的作用范围是在BEGIN...END中。

4.1.1 定义变量

在存储过程中使用declare语句定义变量:

DECLARE 变量名 变量类型 [DEFAULT val];

如果没有 DEFAULT 子句,初始值为NULL 。

4.1.2 为变量赋值

MySQL中使用SET语句为变量赋值:

SET 变量名=表达式;

例如:

DECLARE var1, var2, var3 INT;
SET var1 = 10, var2 = 20;
SET var3 = var1 + var2;

另外还有一种给变量赋值的方法:

SELECT 字段1,字段2... INTO 变量1,变量2... FROM table_name WHERE condition

这个SELECT语法把选定的列直接存储到对应位置的变量中。

4.1.3 用户级变量

前面用DECLARE定义的变量是局部变量,只能在BEGIN...END之间起作用。而用户变量对于该连接的用户来说是全局的。

用户变量通常用@var_name表示。用户变量与连接有关,一个客户端定义的变量不能被其它客户端看到或使用。当客户端退出时,该客户端连接的所有变量将自动释放。

创建用户变量不需要事先声明,直接SET赋值即可:

SET @var_name = expr [, @var_name = expr] ...
  • 在SET语句中,可以使用=:=进行赋值。
  • 在非SET语句中,只能使用:=进行赋值,因为=被视为一个比较操作符。

4.2 流程控制的使用

MySQL中的流程控制语句有:IF语句、CASE语句、LOOP语句、WHILE语句、LEAVE语句、ITERATE语句和REPEAT语句。

4.2.1 IF语句

语法格式如下:

IF expr_condition THEN statement_list
[ELSEIF expr_condition THEN statement_list]...
[ELSE statement_list]
END IF;

4.2.2 CASE语句

CASE语句有两种语法格式,第一种如下:

CASE expr
WHEN value1 THEN statement_list
WHEN value2 THEN statement_list
...
[ELSE statement_list]
END CASE;

第二种如下:

CASE
WHEN expr_condition1 THEN statement_list
WHEN expr_condition2 THEN statement_list
...
[ELSE statement_list]
END CASE;

注意:在存储程序里的 CASE 语句 与 直接在SELECT查询里使用的 CASE 函数有略微的不同。在存储程序里的 CASE 语句不能有ELSE NULL子句,并且用END CASE而不是END来终止。

4.2.3 LOOP语句

LOOP语句的语法如下:

[loop_label:]LOOP
statement_list
END LOOP [loop_label]

loop_label是LOOP语句的标签,该参数可以省略。 LOOP 内的语句一直重复执行直到退出循环,退出循环使用LEAVE语句。

4.2.4 LEAVE语句

LEAVE语句用来退出任何被标注的流程控制语句,语法如下:

LEAVE label;

4.2.5 ITERATE语句

ITERATE语句将执行顺序转到语句段开头处,ITERATE只可以出现在 LOOP、REPEAT和WHILE语句内。语法如下:

ITERATE label;

通俗点讲,就是相当于C++里的continue

4.2.6 REPEAT语句

REPEAT语句创建一个带条件判断的循环过程:

[repeat_label:]REPEAT
statement_list
UNTIL expr_condition
END REPEAT [repeat_label]

4.2.7 WHILE语句

WHILE语句也是创建一个带条件判断的循环过程,不同的是在每次执行循环体时先判断:

[while_label:]WHILE expr_condition DO
statement_list
END WHILE [while_label]

4.3 定义条件和处理程序

特定条件需要特定处理,定义条件是事先定义程序执行过程中遇到的问题,处理程序定义了在遇到这些问题时应当采取的处理方式,这样可以保证存储过程或函数在遇到警告或错误时能继续执行。

4.3.1 定义条件

定义条件也是使用DECLARE语句:

DECLARE condition_name CONDITION FOR SQLSTATE 'sqlstate_value' | mysql_error_code;

sqlstate_value 和 mysql_error_code 都可以表示MySQL的错误,例如:ERROR 1064(42000)中,sqlstate_value的值是42000,mysql_error_code的值是1064。

这个语句指定需要特殊处理的条件。它将一个名字和指定的错误条件关联起来,这个名字可以用在后面的处理程序中。例如:定义'ERROR 1064(42000)'错误名称为syntax_error

DECLARE syntax_error CONDITION FOR SQLSTATE '42000';  /*方法一*/
DECLARE syntax_error CONDITION FOR 1064; /*方法二*/

4.3.2 定义处理程序

定义处理程序语法如下:

DECLARE handler_type HANDLER FOR condition_value sp_statement;
  • handler_type:表示错误处理方式,只能取以下3个值。

    • CONTINUE:遇到错误不处理,继续执行;
    • EXIT:遇到错误马上退出;
    • UNDO:遇到错误后撤回之前的操作,MySQL暂不支持。
  • condition_value:表示错误类型,可以有以下值:
    • SQLSTATE 'sqlstate_value'
    • mysql_error_code
    • condition_name:自定义的条件名称
    • SQLWARNING:匹配所有以01开头的SQLSTATE错误代码
    • NOT FOUND:匹配所有以02开头的SQLSTATE错误代码
    • SQLEXCEPTION:匹配所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE错误代码
  • sp_statement:程序语句段,表示在遇到定义的错误时,需要执行的存储过程或函数。

4.4 光标的使用

查询语句可能返回多条记录,如果数据量非常大,需要使用光标(cursor)来逐条读取查询结果集中的记录。

4.4.1 声明光标

光标必须在打开之前被声明,并且其中用到的变量或条件必须在声明光标之前被声明。MySQL中使用DECLARE关键字来声明光标:

DECLARE cursor_name CURSOR FOR select_statement;

其中的 SELECT 语句返回一个用于创建光标的结果集。

4.4.2 打开光标

打开光标的语法如下:

OPEN cursor_name;

4.4.3 使用光标

通过FETCH关键字从光标中逐条读取到变量中:

FETCH cursor_name INTO var_name[,...];

变量var_name必须在声明光标之前就定义好。

4.4.4 关闭光标

关闭光标的语法如下:

CLOSE cursor_name;

**注意:**MySQL中光标只能在存储过程和函数中使用。

4.4.5 示例

workers表的基础上,创建一个存储过程,根据输入的城市名,输出该城市所有员工的名字。

mysql> DELIMITER //
mysql> CREATE PROCEDURE useCursorDemo(IN city_name VARCHAR(15))
-> BEGIN
-> DECLARE m_name VARCHAR(10);
-> DECLARE m_city VARCHAR(15);
-> DECLARE m_stop INT DEFAULT 0;
-> DECLARE mycursor CURSOR FOR SELECT name,city FROM workers WHERE city=city_name;
-> DECLARE CONTINUE HANDLER FOR NOT FOUND SET m_stop=1;
-> OPEN mycursor;
-> FETCH mycursor INTO m_name,m_city;
-> WHILE m_stop!=1 DO
-> SELECT m_name,m_city;
-> FETCH mycursor INTO m_name,m_city;
-> END WHILE;
-> CLOSE mycursor;
-> END//
Query OK, 0 rows affected (0.08 sec) mysql> DELIMITER ;

调用useCursorDemo存储过程:

mysql> CALL useCursorDemo('Chicago');
+--------+---------+
| m_name | m_city |
+--------+---------+
| Nina | Chicago |
+--------+---------+
1 row in set (0.05 sec) +--------+---------+
| m_name | m_city |
+--------+---------+
| Tim | Chicago |
+--------+---------+
1 row in set (0.05 sec)

通过这个例子,我们可以了解到如何在存储过程或函数中使用变量、光标和流程控制。

五、修改、删除存储过程和函数

使用ALTER语句可以修改存储过程或函数的特性,语法如下:

ALTER {PROCEDURE|FUNCTION} sp_name [characteristic]

sp_name是存储过程或函数的名称,characteristic指定存储过程或函数的特性,与创建过程的参数取值是一样的。

使用DROP语句删除存储过程或函数,语法如下:

DROP {PROCEDURE|FUNCTION} [IF EXISTS] sp_name;

附:MySQL存储过程和函数有什么区别?

在本质上它们都是存储程序。

  1. 函数只能通过 return 语句返回单个值或表对象;而存储过程不允许执行 return,但可以通过 OUT 参数返回多个值。
  2. 函数限制比较多,不能用临时表,只能用表变量,还有一些函数都不可用等等;而存储过程的限制相对就比较少。
  3. 函数可以嵌入在SQL语句中使用,可以在SELECT语句中作为查询语句的一个部分调用;而存储过程一般是作为一个独立的部分来执行。

个人站点:http://songlee24.github.com

MySQL基础笔记(六) 存储过程与函数的更多相关文章

  1. MYSQL基础笔记(六)- 数据类型一

    数据类型(列类型) 所谓数据烈性,就是对数据进行统一的分类.从系统角度出发时为了能够使用统一的方式进行管理,更好的利用有限的空间. SQL中讲数据类型分成三大类:1.数值类型,2.字符串类型和时间日期 ...

  2. MYSQL基础笔记(五)- 练习作业:站点统计练习

    作业:站点统计 1.将用户的访问信息记录到文件中,独占一行,记录IP地址 <?php //站点统计 header('Content-type:text/html;charset=utf-8'); ...

  3. MYSQL基础笔记(四)-数据基本操作

    数据操作 新增数据:两种方案. 1.方案一,给全表字段插入数据,不需要指定字段列表,要求数据的值出现的顺序必须与表中设计的字段出现的顺序一致.凡是非数值数据,到需要使用引号(建议使用单引号)包裹. i ...

  4. MYSQL基础笔记(三)-表操作基础

    数据表的操作 表与字段是密不可分的. 新增数据表 Create table [if not exists] 表名( 字段名 数据类型, 字段名 数据类型, 字段n 数据类型 --最后一行不需要加逗号 ...

  5. MYSQL基础笔记(二)-SQL基本操作

    SQL基本操作 基本操作:CRUD,增删改查 将SQL的基本操作根据操作对象进行分类: 1.库操作 2.表操作 3.数据操作 库操作: 对数据库的增删改查 新增数据库: 基本语法: Create da ...

  6. MYSQL基础笔记(一)

    关系型数据库概念: 1.什么是关系型数据库? 关系型数据库:是一种建立在关系模型(数学模型)上的数据库 关系模型:一种所谓建立在关系上的模型. 关系模型包含三个方面: 1.数据结构:数据存储的问题,二 ...

  7. MySql(三)存储过程和函数

    MySql(三)存储过程和函数 一.什么是存储过程和函数 二.存储过程和函数的相关操作 一.什么是存储过程和函数 存储过程和函数是事先经过编译并存储在数据库中的一段SQL语句的集合,调用存储过程和函数 ...

  8. mysql 开发基础系列17 存储过程和函数(上)

    一. 概述 存储过程和函数是事先经过编译并存储在数据库中的一段sql语句集合,可以简化应用开发人员的很多工作,减少数据在数据库与应用服务器之间的传输,提高数据处理效率是有好处的.存储过程和函数的区别在 ...

  9. mysql 开发基础系列18 存储过程和函数(下)

    1. 光标的使用(游标) 在存储过程和函数中可以使用光标对结果集进行循环的处理,光标使用包括光标的声明,open ,fetch,close. 下面在存储过程中使用一个光标, 这个举例中光标里的逻辑不重 ...

随机推荐

  1. 设计模式:命令模式(Command Pattern)

    问题 某个类中需要定义一个方法,该方法要实现的功能不确定的,需要等到程序执行该方法的时候才确定下来. 例如:定义一个计算数组的方法,可能需要遍历输出数组,也有可能是需要对数组中元素求和. 解决方案 按 ...

  2. 特殊权限和facl

    目 录 第1章 FACL访问控制    1 1.1 FACL简介    1 1.2 getfacl命令查看acl权限    1 1.3 setfacl设置文件的cal权限    1 1.4 批量添加a ...

  3. Python装饰器粗解学习

    此次学习资料详细来自:http://blog.csdn.net/u013471155 本次是粗学,仍有诸多疑问,暂且记录一二,如有不足和建议,希望可以达者指点. 三个关键点理解:   1.关于函数“变 ...

  4. 06 PhantomJS浏览器

    PhantomtomJS PhantomJS是一款无界面浏览器,其自动化操作流程和谷歌浏览器是一致的.由于是无界面的,为了能够展示自动化操作流程,PhantomJS为用户提供了一个截屏的功能,使用sa ...

  5. tensorflow with gpu 环境配置

    1.准备工作 1.1 确保GPU驱动已经安装 lspci | grep -i nvidia 通过此命令可以查看GPU信息,测试机已经安装GPU驱动

  6. 《算法导论》— Chapter 6 堆排序

    序 本文主要介绍堆排序算法(HeapSort),堆排序像合并排序而不像插入排序,堆排序的运行时间为O(nlgn):像插入排序而不像合并排序,它是一种原地(in place)排序算法.在任何时候,数组中 ...

  7. [图文教程] 使用Git 提交项目到码云

    目录 1. 环境准备 2. 开发工具配置Git和SSH 3. 配置SSH到码云 4. 创建一个项目 5. Clone项目到本地 6. Push项目到码云 1. 环境准备 1.1 本机配置Git Hom ...

  8. 有上下界的网络流 loj115 loj116 loj 117

    参考文章 无源汇有上下界的可行流 有源汇有上下界的最大流 有源汇有上下界的最小流 无源汇有上下界可行流 以 loj115 为例. 剥离出必要边与自由边. #include <iostream&g ...

  9. 跟初学者学习IbatisNet第二篇

    在上一篇里面我们知道了什么是IbatisNet,并且知道了如何用IbatisNet进行简单的增删改查的操作,在这一篇文章里面我们主要介绍一下IbatisNet操作存储过程. 我们一般把存储过程分为两种 ...

  10. 大数据学习——下载集群根目录下的文件到E盘

    代码如下: package cn.itcast.hdfs; import java.io.IOException; import org.apache.hadoop.conf.Configuratio ...