MySQL 游标

 

  • SQL语句是“面向集合编程”,重点在于“获得什么”,而不是“如何获得”。
  • 有时候我们不需要对查询结构集的每一条都进行相同的操作,而是只操作其中的某些行,这时候就需要面向过程的编程方法,而游标就是面向过程编程方式的体现
  • 游标就相当于“指针”,它一次只指向一行
  • 游标的作用就是用于对查询数据库所返回的记录进行遍历,以便进行相应的操作

游标的使用

声明(给定结果集)、打开、通过游标获取数据、关闭、释放游标

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。
  • 声明,给定结果集,存储过程结束后游标就被清理

    DECLARE cursor_name CURSOR FOR (SELECT...);
    
  • 打开游标,将结果集送到游标工作区

    OPEN cursor_name;
    
  • 通过游标获取数据

    游标先判断当前行是否为空,若为空则改变done,若不为空则将数据存放到临时变量中,读取后进入下一行为下次读取做准备。

    一定要想明白FETCH的运行方式,不然可能会出现重复FETCH到数据的情况

    FETCH cursor_name INTO (变量名s);
    
  • 关闭游标

    CLOSE cursor_name;
    
  • 因为每次调用FETCH游标就会尝试下一行,因此还要声明一个 NOT FOUND处理程序来处理游标读取不到下一行的情况

    DECLARE CONTINUE HANDLER FOR not found SET done = true;
    

    或者

    DECLARE CONTINUE HANDLER FOR SQLSTATE ‘02000’ SET done = true;
    

    SQLSTATE '02000'可以看做和not found一样,SQLSTATE '02000'是当没有更多的行以供循环时出现的一个条件。

    上面两种都可行,至于done赋值为true还是为1,就要看done是如何定义的,当然定义为INIT的为可以初始化为true

     

使用举例1

现有如下数据表

mysql> select * from t;
+----+------+-----------------+
| id | nums | content         |
+----+------+-----------------+
|  1 |    2 | NULL            |
|  2 |    6 | 二六一十二      |
|  3 |    3 | 三三得九        |
+----+------+-----------------+
3 rows in set (0.00 sec)

我们通过游标读取每一行并实现将nums导入到新数据表中,在实际工作中这种方式可以大大减少我们"分表"时的操作难度。

DELIMITER //

CREATE PROCEDURE transferData()
BEGIN
DECLARE temp_num INT;
DECLARE done BOOLEAN DEFAULT false;
DECLARE cur CURSOR FOR select nums FROM t;
DECLARE continue HANDLER FOR not found SET done = true;

CREATE TABLE IF NOT EXISTS t2 (auto_id int PRIMARY KEY AUTO_INCREMENT, val int not null);

OPEN cur;
REPEAT
IF NOT done THEN
FETCH cur INTO temp_num;
INSERT INTO t2 (val) VALUES (temp_num);
END IF;
UNTIL done=true END REPEAT;
CLOSE cur;
SELECT * from t2;
END//

DELIMITER ;

执行该存储过程后自动显示如下结果:

mysql> select * from t2;
    -> //
+---------+-----+
| auto_id | val |
+---------+-----+
|       1 |   2 |
|       2 |   6 |
|       3 |   3 |
|       4 |   3 |
+---------+-----+
4 rows in set (0.00 sec)

我们会发现,为什么这里多了一行数据?看起来是被重复FETCH并插入了,究其原因,是因为没有理清楚FETCH的运作方式---没有搞清楚什么时候FETCH会改变done

如果我们改成

CREATE PROCEDURE transferData2()
BEGIN
DECLARE temp_num INT;
DECLARE done BOOLEAN DEFAULT false;
DECLARE cur CURSOR FOR select nums FROM t;
DECLARE continue HANDLER FOR not found SET done = true;

CREATE TABLE IF NOT EXISTS t3 (auto_id int PRIMARY KEY AUTO_INCREMENT, val int not null);

OPEN cur;
REPEAT
FETCH cur INTO temp_num;
IF NOT done THEN
INSERT INTO t3 (val) VALUES (temp_num);
END IF;
UNTIL done=true END REPEAT;
CLOSE cur;
SELECT * from t3;
END//

则结果会符合我们的预期

mysql> select * from t3//
+---------+-----+
| auto_id | val |
+---------+-----+
|       1 |   2 |
|       2 |   6 |
|       3 |   3 |
+---------+-----+
3 rows in set (0.00 sec)

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄