MySQL 游标
- SQL语句是“面向集合编程”,重点在于“获得什么”,而不是“如何获得”。
- 有时候我们不需要对查询结构集的每一条都进行相同的操作,而是只操作其中的某些行,这时候就需要面向过程的编程方法,而游标就是面向过程编程方式的体现
- 游标就相当于“指针”,它一次只指向一行
- 游标的作用就是用于对查询数据库所返回的记录进行遍历,以便进行相应的操作
游标的使用
声明(给定结果集)、打开、通过游标获取数据、关闭、释放游标
-
声明,给定结果集,存储过程结束后游标就被清理
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)