Q:為什么要使用游標?
A:
在存儲過程(或函數)中,如果某條select語句返回的結果集中只有1行,可以使用select into語句(上幾篇博客有介紹到用法)來得到該行進行處理;如果結果集中有多行,簡單的select語句成批地進行處理,需要在檢索出來的行中前進或后退一行或多行……若是想得到其中的每一行進行處理,就必須使用游標。
Q:什么是游標?
A:
游標(cursor),是一個存儲在MySQL服務器上的數據庫查詢,游標不是一條 SELECT語句,而是被該語句檢索出來的結果集;可以看做是指向查詢結果集的指針;通過cursor,就可以一次一行的從結果集中把行拿出來處理。
注意:MySQL游標只能用於存儲過程和函數。
游標的處理過程:4步
①聲明游標declare:沒有檢索數據,只是定義要使用的select語句
②打開游標open:打開游標以供使用,用上一步定義的select語句把數據實際檢索出來
③檢索游標fetch:對於填有數據的游標,根據需要取出(檢索)各行
④關閉游標close:在結束游標使用時,必須關閉游標
DECLARE cursor_name CURSOR FOR select_statement;
聲明一個游標cursor_name,讓其指向查詢select_statement的結果集。
注意:
①游標聲明必須出現在變量和條件聲明的后面,但是在異常處理聲明的前面
②一個過程中可以有多個游標聲明
OPEN cursor_name;
cursor_name是聲明中定義的名字;打開游標時才執行相應的select_statement。
FETCH cursor_name INTO var_name [, var_name] ...
從游標cursor_name中拿出一行,把該行的各個列值保存到各個變量中。
解析:
一次只拿一行,拿完后,自動移動指針到下一行;
如果沒有拿到行,會拋出異常,其SQLSTATE代碼值為‘02000’,此時要檢測到該情況,需要聲明異常處理程序 (針對條件NOT FOUND也可以),通常需要在一個循環中來執行fetch語句,通過檢測以上異常來結束循環。
CLOSE cursor_name;
收回游標占用的內存,別浪費資源嘛。
例1:創建過程,計算players表中行的數量
mysql> delimiter $$ mysql> create procedure number_of_players( -> out pnumber int) -> begin -> declare a_playerno int; -> declare found bool default true; 循環控制變量,其值為false時循環結束 ->
-> declare c_players cursor for
-> select playerno from PLAYERS; ①聲明游標 ->
-> declare continue handler for not found -> set found=false; 聲明異常處理程序 ->
-> set pnumber=0; ->
-> open c_players; ②打開游標 ->
-> fetch c_players into a_playerno; ③檢索游標(檢索第一行) -> while found do
-> set pnumber=pnumber+1; -> fetch c_players into a_playerno; -> end while; 循環檢索其余行 ->
-> close c_players; ④關閉游標 -> end$$ mysql> delimiter ; mysql> call number_of_players(@pnumber); mysql> select @pnumber; +----------+
| @pnumber |
+----------+
| 14 |
+----------+ mysql> select count(*) from PLAYERS; +----------+
| count(*) |
+----------+
| 14 |
+----------+
例2:創建過程,計算某個球員的罰款次數--游標聲明中可以包含變量
mysql> delimiter $$ mysql> create procedure number_penalties( -> in p_playerno int, -> out pnumber int) -> begin -> declare a_playerno int; -> declare found bool default true; 循環控制變量 ->
-> declare c_players cursor for 聲明游標 -> select playerno -> from PENALTIES -> where playerno = p_playerno; 包含變量p_playerno ->
-> declare continue handler for not found -> set found=false; 聲明異常處理程序 ->
-> set pnumber=0; ->
-> open c_players; 打開游標 ->
-> fetch c_players into a_playerno; -> while found do 循環檢索游標每一行 -> set pnumber=pnumber+1; -> fetch c_players into a_playerno; -> end while; ->
-> close c_players; 關閉游標 -> end$$ mysql> delimiter ; mysql> call number_penalties(44,@pnumber); mysql> select @pnumber; +----------+
| @pnumber |
+----------+
| 3 |
+----------+