Mysql(8)—游標
上一遍博客寫了有關存儲過程的語法知識 Mysql(7)---存儲過程
游標或許你在工作中很少用到,但用不到不代表不去了解它,但你真正需要它來解決問題的時候,再花時間去學習很可能會影響你的工作進度。
注意:MySQL游標只能用於存儲過程(和函數)。游標主要用於交互式應用。
一、概述
1、定義
游標是一個存儲在MySQL服務器上的數據庫查詢,它不是一條select語句,而是被該語句所檢索出來的結果集。
接下來會對這句話做出進一步解釋。
2、游標的作用
比如有這么個語句
SELECT name,age from person where age>10;
這個語句返回的很可能是多條語句,那么我如何遍歷每一條數據呢,這個時候就需要游標,游標可以理解成java的List<Object>集合,存儲了每一個含有"name"和"age"的對象。接下來我們就可以遍歷集合中每一個對象獲取"name"和"age"。
3、使用游標
使用游標可以大致分為這么幾步
聲明游標 :這個過程實際上是沒有遍歷數據的,它只是定義要使用的select語句來獲取數據。
打開游標 : 上面定義好后,那么這里就需要打開游標。這個過程用前面定義的select語句把數據實際檢索出來。即這個步驟之后,我們就可以遍歷游標中的數據了。
遍歷數據 : 對於有數據的游標,根據需要取出各行的數據來進行一定的操作。
關閉游標 : 使用完游標后,一定要關閉游標。
4、游標語法
**1)聲明游標 **
DECLARE cursor_name CURSOR FOR select_statement
這個語句聲明一個游標。也可以在子程序中定義多個游標,但是一個塊中的每一個游標必須有唯一的名字。聲明游標后也是單條操作的,但是SELECT語句不能有INTO子句。
2) 打開游標
OPEN cursor_name ;
這個語句打開先前聲明的游標。
3) 遍歷數據
FETCH cursor_name INTO var_name ;
這個語句用指定的打開游標讀取下一行(如果有下一行的話),並且前進游標指針。
4) 關閉游標
CLOSE cursor_name ;
下面會用一個詳細的例子來說明,通過這個例子就能很明白游標是什么了。
二、舉例
例子所需表
CREATE TABLE IF NOT EXISTS `store` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
`count` int(11) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=7;
INSERT INTO `store` (`id`, `name`, `count`) VALUES
(1, 'android', 15),
(2, 'iphone', 14),
(3, 'iphone', 20),
(4, 'android', 5),
(5, 'android', 13),
(6, 'iphone', 13);
1、例一
目的:我們現在要用存儲過程做一個功能,統計iphone的總庫存是多少,並把總數輸出到控制台。
delimiter $ # 申明結束標志
drop procedure if exists StatisticStore$ # 如果存儲過程已經存在則刪除
CREATE PROCEDURE StatisticStore()
BEGIN
# 創建接收游標數據的變量
declare c int; # 獲取單條紀錄的數量
declare n varchar(20); #獲取名稱
# 創建總數變量
declare total int default 0;
# 創建結束標志變量
declare done int default false;
# 創建游標 獲取name和count的集合
declare cur cursor for select name,count from store where name = 'iphone';
# 指定游標循環結束時的返回值
declare continue HANDLER for not found set done = true;
# 設置初始值
set total = 0;
# 打開游標
open cur;
# 開始循環游標里的數據
read_loop:loop
# 根據游標當前指向的一條數據 插入到上面申明的局部變量中
fetch cur into n,c;
# 判斷游標的循環是否結束
if done then
leave read_loop; # 跳出游標循環
end if;
# 獲取一條數據時,將count值進行累加操作,這里可以做任意你想做的操作,
set total = total + c;
# 有loop 就一定要有end loop
end loop;
# 關閉游標
close cur;
# 輸出結果
select total;
END $
# 調用存儲過程
call StatisticStore()$
看輸出結果 完美

在啰嗦幾句
fetch:是獲取游標當前指向的數據行,並將指針指向下一行,當游標已經指向最后一行時繼續執行會造成游標溢出。
使用loop循環游標時,他本身是不會監控是否到最后一條數據了,像下面代碼這種寫法,就會造成死循環;
read_loop:loop
fetch cur into n,c;
set total = total+c;
end loop;
在MySql中,造成游標溢出時會引發mysql預定義的NOT FOUND錯誤,所以在上面使用下面的代碼指定了當引發not found錯誤時定義一個continue 的事件,指定這個事件發生時修改done變量的值。
declare continue HANDLER for not found set done = true;
所以在循環時加上了下面這句代碼:
#判斷游標的循環是否結束
if done then
leave read_loop; #跳出游標循環
end if;
如果done的值是true,就結束循環。繼續執行下面的代碼。
2、游標嵌套例子
delimiter $ # 申明結束標志
drop procedure if exists StatisticStore3$
CREATE PROCEDURE StatisticStore3()
BEGIN
declare _n varchar(20);
declare done int default false;
declare cur cursor for select name from store group by name; #其實就是獲得了兩個名稱的集合[android,iphone]
declare continue HANDLER for not found set done = true;
open cur;
read_loop:loop
fetch cur into _n;
if done then
leave read_loop;
end if;
begin
declare c int;
declare n varchar(20);
declare total int default 0;
declare done1 int default false;
declare cur cursor for select name,count from store where name = _n;
declare continue HANDLER for not found set done1 = true;
set total = 0;
open cur; # 這里游標名稱和上面重復了,mysql默認就近原則 所以遍歷的是最近的那么 但還是非常不建議取一樣的名稱
iphone_loop:loop
fetch cur into n,c;
if done1 then
leave iphone_loop;
end if;
set total = total + c;
end loop;
close cur;
select _n,n,total; # select 只輸出一次,而且是第一次遍歷數據
end;
end loop;
close cur;
END$
call StatisticStore3()$
執行結果(select 只輸出一次,而且是第一次遍歷數據)

參考
只要自己變優秀了,其他的事情才會跟着好起來(少將10)
