1、存储过程

存储过程是什么,简单来讲,就像Java中的方法(函数),不过它是SQL世界中的方法。

大部分时候,我们所使用都是单条SQL,用来针对一个或多表连接。但是也有情况,是据判断先对表A执行操作,变动后再结合表B进行操作。即SQL的执行可能需要考虑包含业务规则在内的智能处理。封装操作的好处就不过多说明,无非是简化,复用,降低耦合等,同时,它还具有更高的性能。

考虑这种业务情况,现在你需要获得订单总额,但同时需要增加营业税,且只针对某些顾客,那么你需要:
  • 获得基本的订单总额
  • 将营业税有条件地添加到合计中
  • 返回合计

1.1 基本语句

先看基本的语句,然后再看示例,就豁然开朗了:
--创建存储过程
CREATE PROCEDURE <存储过程的名称>(<变量的类型定义>)
BEGIN
<执行操作>
END; --执行存储过程
CALL <存储过程的名称>(<@变量名>); --删除存储过程
DROP PROCEDURE <存储过程的名称>;
11
 
1
--创建存储过程 
2
CREATE PROCEDURE <存储过程的名称>(<变量的类型定义>)
3
BEGIN
4
  <执行操作>
5
END;
6

7
--执行存储过程
8
CALL <存储过程的名称>(<@变量名>);
9

10
--删除存储过程
11
DROP PROCEDURE <存储过程的名称>;

1.2 创建

然后,根据刚才我们说到的返回包含营业税的订单总额,创建如下存储过程:
-- Name: ordertotal
-- Parameters: onumber = order number
-- taxable = 0 if not taxable, 1 if taxable
-- ototal = order total variable CREATE PROCEDURE ordertotal(
IN onumber INT,
IN taxable BOOLEAN,
OUT ototal DECIMAL(8, 2)
) COMMENT 'Obtain order total, optionally adding tax'
BEGIN
--Declare variable for total
DECLARE total DECIMAL(8, 2);
--Declare tax percentage
DECLARE taxrate INT DEFAULT 6; --GET the order total
SELECT Sum(item_price*quantity)
FROM orderitems
WHERE order_num = onumber
INTO total; --Is this taxable
IF taxable THEN
SELECT total+(total/100*taxrate) INTO total;
END IF; SELECT total INTO ototal;
END;
29
 
1
-- Name: ordertotal
2
-- Parameters: onumber = order number
3
--             taxable = 0 if not taxable, 1 if taxable
4
--             ototal  = order total variable
5

6
CREATE PROCEDURE ordertotal(
7
  IN  onumber INT,
8
  IN  taxable BOOLEAN,
9
  OUT ototal DECIMAL(8, 2)
10
) COMMENT 'Obtain order total, optionally adding tax'
11
BEGIN
12
  --Declare variable for total
13
  DECLARE total DECIMAL(8, 2);
14
  --Declare tax percentage
15
  DECLARE taxrate INT DEFAULT 6;
16
  
17
  --GET the order total
18
  SELECT Sum(item_price*quantity)
19
  FROM orderitems
20
  WHERE order_num = onumber
21
  INTO total;
22

23
  --Is this taxable
24
  IF taxable THEN
25
    SELECT total+(total/100*taxrate) INTO total;
26
  END IF;
27

28
  SELECT total INTO ototal;
29
END;

看起来这么长好像挺唬人,其实很清楚:
CREATE PROCEDURE ordertotal(
IN onumber INT,
IN taxable BOOLEAN,
OUT ototal DECIMAL(8, 2)
) COMMENT 'Obtain order total, optionally adding tax'
5
 
1
CREATE PROCEDURE ordertotal(
2
  IN  onumber INT,
3
  IN  taxable BOOLEAN,
4
  OUT ototal DECIMAL(8, 2)
5
) COMMENT 'Obtain order total, optionally adding tax'
  • 使用CREATE PROCEDURE关键词创建了名为ordertotal的存储过程
  • 该存储过程定义了三个变量,IN表示要求输入的参数,OUT表示输出的结果。INT、BOOLEAN等表示变量的数据类型
  • COMMENT非必需,如果有,那么在SHOW PROCEDURE STATUS的结果时会显示(简单说,类似于方法的说明)

BEGIN
...
END;
3
 
1
BEGIN
2
  ...
3
END;
  • BEGIN和END用来界定存储过程操作执行的语句

  --Declare variable for total
DECLARE total DECIMAL(8, 2);
--Declare tax percentage
DECLARE taxrate INT DEFAULT 6; --GET the order total
SELECT Sum(item_price*quantity)
FROM orderitems
WHERE order_num = onumber
INTO total;
10
 
1
  --Declare variable for total
2
  DECLARE total DECIMAL(8, 2);
3
  --Declare tax percentage
4
  DECLARE taxrate INT DEFAULT 6;
5
  
6
  --GET the order total
7
  SELECT Sum(item_price*quantity)
8
  FROM orderitems
9
  WHERE order_num = onumber
10
  INTO total;
  • DECLARE用来定义存储过程中的局部变量
  • INTO表示赋值到变量

  --Is this taxable
IF taxable THEN
SELECT total+(total/100*taxrate) INTO total;
END IF;
4
 
1
  --Is this taxable
2
  IF taxable THEN
3
    SELECT total+(total/100*taxrate) INTO total;
4
  END IF;
  • IF <boolean> THEN <do something> END IF 为条件执行语句,记得END IF结尾

假如用Java来写的话,大概是这么个意思:
public void ordertotal(int onumber, boolean taxable, double ototal) {

    double total;
int taxrate = 6; total = getOrderTotal(onumber);
if (taxable) {
total += total / (100 * taxrate);
} ototal = total;
}
12
 
1
public void ordertotal(int onumber, boolean taxable, double ototal) {
2

3
    double total;
4
    int taxrate = 6;
5
    
6
    total = getOrderTotal(onumber);
7
    if (taxable) {
8
        total += total / (100 * taxrate);
9
    }
10

11
    ototal = total;
12
}

1.3 执行

在1.2我们定义了存储过程ordertotal(),则执行方式为:
--不含营业税
CALL ordertotal(20005, 0, @total);
SELECT @total +----------+
| @total |
+----------+
| 149.87 |
+----------+ --包含营业税
CALL ordertotal(20005, 1, @total);
SELECT @total +-----------------+
| @total |
+-----------------+
| 158.862200000 |
+-----------------+
19
 
1
--不含营业税
2
CALL ordertotal(20005, 0, @total);
3
SELECT @total
4

5
+----------+
6
|  @total  |
7
+----------+
8
|  149.87  |
9
+----------+
10

11
--包含营业税
12
CALL ordertotal(20005, 1, @total);
13
SELECT @total
14

15
+-----------------+
16
|      @total     |
17
+-----------------+
18
|  158.862200000  |
19
+-----------------+

定义时我们说过,IN表示定义输入,OUT表示定义输出,所以这里的三个变量中,前两者由调用者传入,而第三个变量,则作为返回结果的变量。
(调用存储过程时,用于临时存储返回数据的变量必须以@开头)

1.4 检查

用来显示“创建某个存储过程的CREATE语句”,使用SHOW CREATE PROCEDURE语句:
SHOW CREATE PROCEDURE ordertotal;
1
 
1
SHOW CREATE PROCEDURE ordertotal;

1.5 删除

DROP PROCEDURE ordertotal;
1
 
1
DROP PROCEDURE ordertotal;
注意,检查和删除存储过程,都不用加后面的(),只需要给出存储过程的名称即可。



2、游标

在检索出来的行中,前进或者后退一行或多行,就需要用到所谓的“游标”。游标不是某个SELECT语句,但是它是被该语句检索出来的结果集,另外,MySQL游标只能用于存储过程(和函数)。

2.1 创建游标

使用DECLARE和CURSOR关键字:
CREATE PROCEDURE processorders()
BEGIN
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
END;
6
 
1
CREATE PROCEDURE processorders()
2
BEGIN
3
  DECLARE ordernumbers CURSOR
4
  FOR
5
  SELECT order_num FROM orders;
6
END;

2.2 打开和关闭游标

因为游标局限于存储过程,所以如果存储过程处理完成后,游标就会消失。所以往往在存储过程中要关键字OPEN进行打开。另,游标相关的SELECT查询语句,在定义时是不执行的,在OPEN时才执行查询,存储检索出的数据以供浏览和滚动。在游标使用完成后,使用CLOSE进行关闭:
CREATE PROCEDURE processorders()
BEGIN
--Declare
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders; --Open
OPEN ordernumbers; --Close
CLOSE ordernumbers;
END;
13
 
1
CREATE PROCEDURE processorders()
2
BEGIN
3
  --Declare
4
  DECLARE ordernumbers CURSOR
5
  FOR
6
  SELECT order_num FROM orders;
7

8
  --Open
9
  OPEN ordernumbers;
10
  
11
  --Close
12
  CLOSE ordernumbers;
13
END;

2.3 使用游标数据

打开游标之后,我们就可以使用关键字FETCH访问数据了,FETCH是从第一行开始,获取当前行的数据,每次执行后会移动内部行指针,再次调用FETCH则会检索到下一行(不会重复读取同一行):
CREATE PROCEDURE processorders()
BEGIN
--Declare
DECLARE o INT;
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders; --Open
OPEN ordernumbers; --Get
FETCH ordernumbers INTO o; --Close
CLOSE ordernumbers;
END;
17
 
1
CREATE PROCEDURE processorders()
2
BEGIN
3
  --Declare
4
  DECLARE o INT;
5
  DECLARE ordernumbers CURSOR
6
  FOR
7
  SELECT order_num FROM orders;
8
 
9
  --Open
10
  OPEN ordernumbers;
11
 
12
  --Get
13
  FETCH ordernumbers INTO o;  
14
 
15
  --Close
16
  CLOSE ordernumbers;
17
END;

看一个复杂些的例子:
CREATE PROCEDURE processorders()
BEGIN
--Declare local variables
DELCARE done BOOLEAN DEFAULT 0;
DECLARE o INT;
DECLARE t DECIMAL(8,2); --Declare cursor
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders; --Declare continue handler
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1; --Create a table to store the results
CREATE TABLE IF NOT EXISTS ordertotals(order_num INT, total DECIMAL(8,2)); --Open the cursor
OPEN ordernumbers; --Loop through all rows
REPEAT
FETCH ordernumbers INTO o;
CALL ordertotal(o, 1, t);
INSERT INTO ordertotals(order_num, total) VALUES(o, t);
UNTIL done END REPEAT; --Close the cursor
CLOSE ordernumbers;
END;
31
 
1
CREATE PROCEDURE processorders()
2
BEGIN
3
  --Declare local variables
4
  DELCARE done BOOLEAN DEFAULT 0;
5
  DECLARE o    INT;
6
  DECLARE t    DECIMAL(8,2);
7

8
  --Declare cursor
9
  DECLARE ordernumbers CURSOR
10
  FOR
11
  SELECT order_num FROM orders;
12
 
13
  --Declare continue handler
14
  DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
15

16
  --Create a table to store the results
17
  CREATE TABLE IF NOT EXISTS ordertotals(order_num INT, total DECIMAL(8,2));  
18

19
  --Open the cursor
20
  OPEN ordernumbers;
21
 
22
  --Loop through all rows
23
  REPEAT
24
    FETCH ordernumbers INTO o;
25
    CALL ordertotal(o, 1, t);
26
    INSERT INTO ordertotals(order_num, total) VALUES(o, t);
27
  UNTIL done END REPEAT;
28

29
  --Close the cursor
30
  CLOSE ordernumbers;
31
END;
  • 以上存储过程,游标不断读取订单号,并以此为参调用另一个存储过程,将最终的值填入到表ordertotals中
  • CONTINUE HANDLER 是在条件出现时执行的代码,SQLSTATE '02000' 表没有找到更多的行(MySQL错误代码)

SELECT * FROM ordertotals;

+---------+---------+
| 20005 | 158.86 |
| 20006 | 58.30 |
| 20007 | 1060.00 |
| 20008 | 132.50 |
| 20009 | 40.78 |
+---------+---------+
x
 
1
SELECT * FROM ordertotals;
2

3
+---------+---------+
4
|  20005  |  158.86 |
5
|  20006  |   58.30 |
6
|  20007  | 1060.00 |
7
|  20008  |  132.50 |
8
|  20009  |   40.78 |
9
+---------+---------+


《MySQL必知必会》[05] 存储过程和游标的更多相关文章

  1. 《mysql必知必会》读书笔记--存储过程的使用

    以前对mysql的认识与应用只是停留在增删改查的阶段,最近正好在学习mysql相关内容,看了一本书叫做<MySQL必知必会>,看了之后对MySQL的高级用法有了一定的了解.以下内容只当读书 ...

  2. 《MySQL必知必会》整理

    目录 第1章 了解数据库 1.1 数据库基础 1.1.1 什么是数据库 1.1.2 表 1.1.3 列和数据类型 1.1.4 行 1.1.5 主键 1.2 什么是SQL 第2章 MySQL简介 2.1 ...

  3. MySQL使用和操作总结(《MySQL必知必会》读书笔记)

    简介 MySQL是一种DBMS,即它是一种数据库软件.DBMS可分为两类:一类是基于共享文件系统的DBMS,另一类是基于客户机——服务器的DBMS.前者用于桌面用途,通常不用于高端或更关键应用. My ...

  4. mysql学习--mysql必知必会1

     例如以下为mysql必知必会第九章開始: 正則表達式用于匹配特殊的字符集合.mysql通过where子句对正則表達式提供初步的支持. keywordregexp用来表示后面跟的东西作为正則表達式 ...

  5. 《MySQL必知必会》[01] 基本查询

    <MySQL必知必会>(点击查看详情) 1.写在前面的话 这本书是一本MySQL的经典入门书籍,小小的一本,也受到众多网友推荐.之前自己学习的时候是啃的清华大学出版社的计算机系列教材< ...

  6. mysql学习--mysql必知必会

      上图为数据库操作分类:     下面的操作參考(mysql必知必会) 创建数据库 运行脚本建表: mysql> create database mytest; Query OK, 1 row ...

  7. mysql 必知必会总结

    以前 mysql 用的不是很多, 2 天看了一遍 mysql 必知必会又复习了一下基础.  200 页的书,很快就能看完, 大部分知识比较基础, 但还是了解了一些以前不知道的知识点.自己做一个备份,随 ...

  8. 读《MySql必知必会》笔记

    MySql必知必会 2017-12-21 意义:记录个人不注意的,或不明确的,或不知道的细节方法技巧,此书250页 登陆: mysql -u root-p -h myserver -P 9999 SH ...

  9. MySql必知必会内容导图

    <MySQL必知必会>从介绍简单的数据检索开始,逐步深入一些复杂的内容,包括联结的使用.子查询.正则表达式和基于全文本的搜索.存储过程.游标.触发器.表约束,等等.通过重点突出的章节,条理 ...

  10. 读《MySQL必知必会》我学到了什么?

    前言 最近在写项目的时候发现自己的SQL基本功有些薄弱,遂上知乎查询MYSQL关键字,期望得到某些高赞答案的指点,于是乎发现了 https://www.zhihu.com/question/34840 ...

随机推荐

  1. 【c++】删除string中指定的字符

    使用string::iterator(字符串迭代器)从开始 str.begin() 迭代到最后 str.end() ,再使用string.erase(const_iterator p)函数来删除迭代器 ...

  2. parseInt转换

    function parse2Int(num) { return parseInt(num,10); } 如果 string 以 "0x" 开头,parseInt() 会把 str ...

  3. ionic3打包不能prod的问题

    在最近的项目中,我ionic3采用了懒加载,来提高性能.但是当我普通打包的时候,正常成功了,但是加上--prod的时候,就报错了. 报错如下: 大概意思就是page是声明的一部分,然后请在更高级声明之 ...

  4. Java网络编程--套接字Socket

    一.套接字Socket IP地址标志Internet上的计算机,端口号标志正在计算机上运行的进程(程序). 端口号被规定为一个16位的0--65535之间的整数,其中,0--1023被预先定义的服务通 ...

  5. matlab练习程序(FAST特征点检测)

    算法思想:如果一个像素与它邻域的像素差别较大(过亮或过暗) , 那它更可能是角点. 算法步骤: 1.上图所示,一个以像素p为中心,半径为3的圆上,有16个像素点(p1.p2.....p16). 2.定 ...

  6. [20170927]关于hugepages.txt

    [20170927]关于hugepages.txt --//今天测试hugepages与内核参数nr_overcommit_hugepages,才发现HugePages_Surp表示什么? --// ...

  7. Django框架的使用教程--Cookie-Session[五]

    Cookie cookie是存储在浏览器中的一段文本信息,下次同一网站请求,就会发送该cookie给服务器,一般的浏览器都有启动cookie,用cookie存储信息,最好不要存储密码,cookie也有 ...

  8. 解决 Mac 突然没有声音

    前言 偶尔早上上班时发现Mac突然没有声音了,不明所以.虽然重启也可以,但是也不免太麻烦了.也许是人品不太好,遇到过多次这种情况,就在快要砸了的自己mac的时候,脑中灵光一闪,难道这是因为核心音频守护 ...

  9. 卷积运算的本质,以tensorflow中VALID卷积方式为例。

    卷积运算在数学上是做矩阵点积,这样可以调整每个像素上的BGR值或HSV值来形成不同的特征.从代码上看,每次卷积核扫描完一个通道是做了一次四重循环.下面以VALID卷积方式为例进行解释. 下面是pyth ...

  10. Base64编码/解码

    /// <summary> /// Base64编码 /// </summary> /// <param name="str">字符串</ ...