mysql 为游标赋值_mysql 存储过程之游标
游標(biāo)按我的理解就是用在sql編程中對(duì)查詢結(jié)果集的解析,類(lèi)比jdbc中的resultset對(duì)象。FETCH 一行游標(biāo)指針就往下面移動(dòng)一行,直到所有行被遍歷完成。
游標(biāo)的使用分為4步:
1、定義游標(biāo),指定游標(biāo)名和查詢sql語(yǔ)句
2、打開(kāi)游標(biāo)
3、fetch 獲取數(shù)據(jù),賦值給變量
4、關(guān)閉游標(biāo)
基本使用
1、數(shù)據(jù)準(zhǔn)備
CREATE TABLE `goods` (
`gid` int(11) NOT NULL COMMENT '主鍵',
`name` char(5) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品',
`num` int(11) NULL DEFAULT NULL COMMENT '庫(kù)存',
PRIMARY KEY (`gid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of goods
-- ----------------------------
INSERT INTO `goods` VALUES (1, 'cat', 34);
INSERT INTO `goods` VALUES (2, 'dog', 0);
INSERT INTO `goods` VALUES (3, 'pig', 12);
2、定義存儲(chǔ)過(guò)程,簡(jiǎn)單使用下游標(biāo)
CREATE DEFINER="root"@"%" PROCEDURE "p5"()
BEGIN
# 定義接受sql數(shù)據(jù)的變量
DECLARE row_gid int;
DECLARE row_name varchar(20);
DECLARE row_num int;
# 定義游標(biāo)
DECLARE getgoods CURSOR FOR
select gid, name, num from goods;
# 打開(kāi)游標(biāo)
OPEN getgoods;
# 獲取數(shù)據(jù),使用一次FETCH就是獲取一行
# 注意需要按列順序的順序賦值
FETCH getgoods INTO row_gid, row_name, row_num;
select row_gid, row_name, row_num;
FETCH getgoods INTO row_gid, row_name, row_num;
select row_gid, row_name, row_num;
FETCH getgoods INTO row_gid, row_name, row_num;
select row_gid, row_name, row_num;
FETCH getgoods INTO row_gid, row_name, row_num;
select row_gid, row_name, row_num;
# 關(guān)閉游標(biāo)
CLOSE getgoods;
END
執(zhí)行下 CALL p5(); 可以看到有三個(gè)結(jié)果集
image.png
然而我FETCH 了4次,只有3行。那么產(chǎn)生了游標(biāo)越界錯(cuò)誤。
1329 - No data - zero rows fetched, selected, or processed
使用REPEAT循環(huán)去FETCH 數(shù)據(jù)
使用循環(huán)去遍歷游標(biāo),將查詢sql所有的數(shù)據(jù)取出。使用count(*)總記錄數(shù)去做循環(huán)的判斷條件。若循環(huán)計(jì)數(shù)器大于等于總記錄數(shù)則停止循環(huán)。
CREATE DEFINER="root"@"%" PROCEDURE "p6"()
BEGIN
# 定義接受sql數(shù)據(jù)的變量
DECLARE row_gid int;
DECLARE row_name varchar(20);
DECLARE row_num int;
# 總行數(shù)
DECLARE cnt INT DEFAULT 0;
# 循環(huán)變量i
DECLARE i INT DEFAULT 0;
# 定義游標(biāo)
DECLARE getgoods CURSOR FOR select gid, name, num from goods;
# 查詢總行數(shù)并賦值給 cnt
# 注意這句話一定要放到定義游標(biāo)的下面,否則報(bào)錯(cuò)創(chuàng)建存儲(chǔ)過(guò)程失敗!
SELECT COUNT(*) INTO cnt FROM goods;
# 打開(kāi)游標(biāo)
OPEN getgoods;
# 開(kāi)始循環(huán)
REPEAT
SET i := i+1;
FETCH getgoods INTO row_gid, row_name, row_num;
select row_gid, row_name, row_num;
UNTIL i>=cnt END REPEAT;
# 循環(huán)結(jié)束
# 關(guān)閉游標(biāo)
CLOSE getgoods;
END
使用游標(biāo)監(jiān)聽(tīng)器 continue handler 和 exit handler 來(lái)完善游標(biāo)的遍歷
上面遍歷游標(biāo),防止越界讀取。使用的是count(*) 額外的去發(fā)出一條查詢,有著額外的性能損耗。事實(shí)上不需要這樣,mysql游標(biāo)有提供它的方式。
我們可以使用 handler 監(jiān)聽(tīng)器,在該監(jiān)聽(tīng)器中定義sql。等到游標(biāo)被遍歷完便會(huì)自動(dòng)執(zhí)行這個(gè)sql。那么我們可以使用一個(gè)變量,初始化為0,默認(rèn)未遍歷完。在監(jiān)聽(tīng)器sql中寫(xiě)入設(shè)置變量值為1。然后在循環(huán)中使用該變量判斷即可
定義監(jiān)聽(tīng)器
DECLARE CONTINUE HANDLER FOR NOT FOUND set flag :=1;
CREATE DEFINER="root"@"%" PROCEDURE "p7"()
BEGIN
DECLARE row_gid int;
DECLARE row_name varchar(20);
DECLARE row_num int;
# 定義循環(huán)退出標(biāo)志符變量
DECLARE flag INT DEFAULT 0;
DECLARE getgoods CURSOR FOR select gid, name, num from goods;
# 定義監(jiān)聽(tīng)器
DECLARE CONTINUE HANDLER FOR NOT FOUND set flag :=1;
OPEN getgoods;
REPEAT
FETCH getgoods INTO row_gid, row_name, row_num;
select row_gid, row_name, row_num;
UNTIL flag=1 END REPEAT;
CLOSE getgoods;
END
執(zhí)行下 call p7();發(fā)現(xiàn)只有3行記錄卻放回4個(gè)結(jié)果集。而且第3個(gè)和第4個(gè)結(jié)果集是相同的。這是一個(gè)問(wèn)題。
image.png
第4次執(zhí)行fetch時(shí)是沒(méi)數(shù)據(jù)的,那么觸發(fā)not found set flag:=1 ,然后continue一下繼續(xù)執(zhí)行后面的sql語(yǔ)句,那么select row_gid, row_name, row_num;又被執(zhí)行一次,最后一行就被執(zhí)行了2次。
若not found set flag:=1 觸發(fā)后,后面的select不再執(zhí)行即可。那么我們可以使用 exit代替continute,exit和continute的區(qū)別是:exit觸發(fā)后后面的語(yǔ)句不再執(zhí)行!
那么使用exit可以完美解決如下:
CREATE DEFINER="root"@"%" PROCEDURE "p7"()
BEGIN
DECLARE row_gid int;
DECLARE row_name varchar(20);
DECLARE row_num int;
# 定義循環(huán)退出標(biāo)志符變量
DECLARE flag INT DEFAULT 0;
DECLARE getgoods CURSOR FOR select gid, name, num from goods;
# 定義EXIT 監(jiān)聽(tīng)器
DECLARE EXIT HANDLER FOR NOT FOUND set flag :=1;
OPEN getgoods;
REPEAT
FETCH getgoods INTO row_gid, row_name, row_num;
select row_gid, row_name, row_num;
UNTIL flag=1 END REPEAT;
CLOSE getgoods;
END
執(zhí)行如下,3個(gè)返回集沒(méi)問(wèn)題
image.png
除了continue 和exit還有一種undo handler 。undo的功能是觸發(fā)后,前面的語(yǔ)句撤銷(xiāo),目前mysql不支持
除了使用exit解決,continue 基礎(chǔ)上也是可以的。讓如果一定要使用continue 的話就需要修改下邏輯,提前FETCH 下了。為了避免查詢結(jié)果為空而導(dǎo)致返回空結(jié)果集,就需要使用while循環(huán)代替pepeat循環(huán)。因?yàn)閜epeat循環(huán)類(lèi)似do while,它總是先執(zhí)行一次循環(huán)體再判斷的。
那么,while加上continue handler 的正確遍歷方式如下:
CREATE DEFINER="root"@"%" PROCEDURE "p7"()
BEGIN
DECLARE row_gid int;
DECLARE row_name varchar(20);
DECLARE row_num int;
# 定義循環(huán)退出標(biāo)志符變量
DECLARE flag INT DEFAULT 0;
DECLARE getgoods CURSOR FOR select gid, name, num from goods;
# 定義監(jiān)聽(tīng)器
DECLARE CONTINUE HANDLER FOR NOT FOUND set flag :=1;
OPEN getgoods;
# 提前FETCH下
FETCH getgoods INTO row_gid, row_name, row_num;
# 換成while循環(huán),就不會(huì)當(dāng)返回集為null時(shí)查出數(shù)據(jù)為空的了
# 注意while循環(huán)的循環(huán)條件為true時(shí)才進(jìn)入循環(huán)
WHILE flag=0 DO
select row_gid, row_name, row_num;
FETCH getgoods INTO row_gid, row_name, row_num;
END WHILE;
CLOSE getgoods;
END
總結(jié)
以上是生活随笔為你收集整理的mysql 为游标赋值_mysql 存储过程之游标的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python开始print_当你使用pr
- 下一篇: python访问数据库日志文件_pyth